tutorial

120 Menggunakan Enum dan Scalar Kustom di gqlgen

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

FiturEnumScalar Kustom
ValidationValue matching fixedValidasi struktur/type value
Use case populerStatus, role, faseDate, Email, JSON, BigInt
Pengaruh di GraphQLStrongly typedFlexible/loose, custom parse
Otomatis di Go?YaPerlu 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

  1. Selalu Uji Scalar Kustom
    • Edge case parsing (string, null, invalid format, dll)
  2. Batasi Enum
    • Enum hanya untuk set value terbatas & fixed (bukan dinamis)
  3. 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.

comments powered by Disqus