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:
# schema.graphql
enum TransactionStatus {
PENDING
SUCCESS
FAILED
}
type Transaction {
id: ID!
amount: Int!
status: TransactionStatus!
}
type Query {
transaction(id: ID!): Transaction
}
Setelah update schema, jalankan:
go run github.com/99designs/gqlgen generate
gqlgen otomatis membangkitkan enum dalam kode Go:
// generated.go, potongan otmatis
type TransactionStatus string
const (
TransactionStatusPending TransactionStatus = "PENDING"
TransactionStatusSuccess TransactionStatus = "SUCCESS"
TransactionStatusFailed TransactionStatus = "FAILED"
)
Implementasi Resolver Sederhana
func (r *queryResolver) Transaction(ctx context.Context, id string) (*model.Transaction, error) {
return &model.Transaction{
ID: id,
Amount: 150000,
Status: model.TransactionStatusSuccess,
}, nil
}
Simulasi Query
query {
transaction(id: "trx01") {
id
amount
status
}
}
Response:
{
"data": {
"transaction": {
"id": "trx01",
"amount": 150000,
"status": "SUCCESS"
}
}
}
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
scalar DateTime
type UserAction {
id: ID!
actedAt: DateTime!
}
type Query {
action(id: ID!): UserAction
}
2. Tambahkan Implementasi Go-nya
Tambahkan entry di gqlgen.yml:
models:
DateTime:
model:
- github.com/your/module/path/graph/customscalars.DateTime
Kemudian buat file customscalars/datetime.go:
package customscalars
import (
"fmt"
"io"
"time"
)
type DateTime time.Time
const dateFormat = time.RFC3339
func (d *DateTime) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("DateTime must be a string")
}
t, err := time.Parse(dateFormat, str)
if err != nil {
return err
}
*d = DateTime(t)
return nil
}
func (d DateTime) MarshalGQL(w io.Writer) {
t := time.Time(d)
_, _ = io.WriteString(w, fmt.Sprintf("%q", t.Format(dateFormat)))
}
3. Contoh Penggunaan di Resolver
func (r *queryResolver) Action(ctx context.Context, id string) (*model.UserAction, error) {
actedAt := customscalars.DateTime(time.Now())
return &model.UserAction{
ID: id,
ActedAt: actedAt,
}, nil
}
4. Simulasi Query
query {
action(id: "act123") {
id
actedAt
}
}
Response:
{
"data": {
"action": {
"id": "act123",
"actedAt": "2024-06-13T21:17:00Z"
}
}
}
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.