120 Menggunakan Enum dan Scalar Kustom di gqlgen
gqlgen telah menjadi de facto library untuk membangun GraphQL API di Go. Keunggulan utamanya terletak pada strongly-typed, serta kemudahan dalam memperluas konsep dasar GraphQL melalui enum dan scalar kustom.
Pada artikel ke-120 seri GraphQL Engineering, saya akan mengupas tentang penggunaan enum dan scalar kustom dengan gqlgen, lengkap dengan contoh kode, simulasi query, serta diagram proses yang memudahkan pemahaman.
Mengapa Perlu Enum dan Scalar Kustom?
GraphQL secara default memiliki beberapa scalar primitive seperti Int, Float, String, Boolean, dan ID. Namun, dalam praktik nyata, seringkali tipe data yang kita butuhkan lebih kompleks atau memiliki cakupan terbatas, misalnya:
- Enum: Status transaksi (
PENDING,SUCCESS,FAILED) - Custom Scalar:
DateTime,BigInt,Email
Dengan enum, kita memastikan nilai yang diterima hanya subset tertentu. Dengan scalar kustom, kita bisa membatasi dan memvalidasi lebih banyak logic di level schema.
Implementasi Enum di gqlgen
Misalkan kita ingin membuat API pembayaran dengan status transaksi berbasis Enum. Mari mulai dengan mendefinisikan schema:
1# schema.graphql
2enum TransactionStatus {
3 PENDING
4 SUCCESS
5 FAILED
6}
7
8type Transaction {
9 id: ID!
10 amount: Int!
11 status: TransactionStatus!
12}
13
14type Query {
15 transaction(id: ID!): Transaction
16}Setelah update schema, jalankan:
1go run github.com/99designs/gqlgen generategqlgen otomatis membangkitkan enum dalam kode Go:
1// generated.go, potongan otmatis
2type TransactionStatus string
3
4const (
5 TransactionStatusPending TransactionStatus = "PENDING"
6 TransactionStatusSuccess TransactionStatus = "SUCCESS"
7 TransactionStatusFailed TransactionStatus = "FAILED"
8)Implementasi Resolver Sederhana
1func (r *queryResolver) Transaction(ctx context.Context, id string) (*model.Transaction, error) {
2 return &model.Transaction{
3 ID: id,
4 Amount: 150000,
5 Status: model.TransactionStatusSuccess,
6 }, nil
7}Simulasi Query
1query {
2 transaction(id: "trx01") {
3 id
4 amount
5 status
6 }
7}Response:
1{
2 "data": {
3 "transaction": {
4 "id": "trx01",
5 "amount": 150000,
6 "status": "SUCCESS"
7 }
8 }
9}Implementasi Custom Scalar di gqlgen
Kadang, scalar default tidak cukup. Misal, untuk tipe waktu. Biasanya kita ingin scalar DateTime, namun GraphQL sendiri tidak menyediakan tipe itu secara native.
1. Definisikan Scalar di Schema
1scalar DateTime
2
3type UserAction {
4 id: ID!
5 actedAt: DateTime!
6}
7
8type Query {
9 action(id: ID!): UserAction
10}2. Tambahkan Implementasi Go-nya
Tambahkan entry di gqlgen.yml:
1models:
2 DateTime:
3 model:
4 - github.com/your/module/path/graph/customscalars.DateTimeKemudian buat file customscalars/datetime.go:
1package customscalars
2
3import (
4 "fmt"
5 "io"
6 "time"
7)
8
9type DateTime time.Time
10
11const dateFormat = time.RFC3339
12
13func (d *DateTime) UnmarshalGQL(v interface{}) error {
14 str, ok := v.(string)
15 if !ok {
16 return fmt.Errorf("DateTime must be a string")
17 }
18 t, err := time.Parse(dateFormat, str)
19 if err != nil {
20 return err
21 }
22 *d = DateTime(t)
23 return nil
24}
25
26func (d DateTime) MarshalGQL(w io.Writer) {
27 t := time.Time(d)
28 _, _ = io.WriteString(w, fmt.Sprintf("%q", t.Format(dateFormat)))
29}3. Contoh Penggunaan di Resolver
1func (r *queryResolver) Action(ctx context.Context, id string) (*model.UserAction, error) {
2 actedAt := customscalars.DateTime(time.Now())
3 return &model.UserAction{
4 ID: id,
5 ActedAt: actedAt,
6 }, nil
7}4. Simulasi Query
1query {
2 action(id: "act123") {
3 id
4 actedAt
5 }
6}1{
2 "data": {
3 "action": {
4 "id": "act123",
5 "actedAt": "2024-06-13T21:17:00Z"
6 }
7 }
8}Perbandingan Enum vs Scalar Kustom
| Fitur | Enum | Scalar Kustom |
|---|---|---|
| Validation | Value matching fixed | Validasi struktur/type value |
| Use case populer | Status, role, fase | Date, Email, JSON, BigInt |
| Pengaruh di GraphQL | Strongly typed | Flexible/loose, custom parse |
| Otomatis di Go? | Ya | Perlu code tambahan |
Diagram Alur Serialisasi Scalar Kustom
Untuk memperjelas bagaimana scalar kustom diproses, berikut alurnya via mermaid:
flowchart TD
ClientRequest["Client mengirim query dengan value scalar kustom"]
Server("GraphQL Server (gqlgen)")
Unmarshal("UnmarshalGQL() Scalar Kustom")
Resolver("Query Resolver")
Marshal("MarshalGQL() Scalar Kustom")
Response["Server mengirim response ke client"]
ClientRequest --> Server
Server --> Unmarshal
Unmarshal --> Resolver
Resolver --> Marshal
Marshal --> Response
Best Practices
- Selalu Uji Scalar Kustom
- Edge case parsing (string, null, invalid format, dll)
- Batasi Enum
- Enum hanya untuk set value terbatas & fixed (bukan dinamis)
- Gunakan Test Table
- Coverage mudah ditambah, scalar mudah diregresi
Kesimpulan
Menggunakan enum dan scalar kustom di gqlgen Go akan membuat API lebih kuat, tipe data lebih safe, serta logic validasi lebih mudah. Scalar kustom memang butuh sedikit boilerplate, namun reward-nya luar biasa, terutama untuk maintainability dan debuggability.
Jangan ragu menambahkan scalar atau enum baru saat schema bertambah kompleks. Namun, tetap jaga agar implementasinya tetap clean dan teruji.
Referensi:
Selamat bereksperimen dengan gqlgen, enum, dan scalar kustom! Jika ada pertanyaan atau pengalaman menarik, bagikan di kolom komentar.