tutorial

21 Best Practice dalam Mendesain Skema GraphQL

21 Best Practice dalam Mendesain Skema GraphQL

GraphQL telah merevolusi cara kita membangun dan mengkonsumsi API, menawarkan fleksibilitas yang sulit diberikan REST. Namun, fleksibilitas ini bisa menjadi pedang bermata dua jika kita tidak hati-hati dalam mendesain skema (schema) GraphQL. Skema yang buruk dapat menyebabkan masalah performa, keamanan, dan user experience yang tidak optimal.

Sebagai seorang engineer yang sudah beberapa tahun bekerja dengan GraphQL — dari scale-up fintech, e-commerce, hingga organisasi nirlaba — saya belajar bahwa ada “jalan ninja” unik dalam mendesain skema GraphQL agar lebih sustainable, scalable, dan mudah dipelihara. Berikut adalah 21 best practice beserta contoh, simulasi, hingga diagram alur yang biasa saya terapkan.


1. Gunakan Nama yang Konsisten dan Deskriptif

Gunakan naming convention yang mudah dipahami tim lintas fungsi. Nama field, type, dan operasi harus konsisten, mudah diingat, serta deskriptif.
Contoh buruk:

type Usr { nm: String }

Contoh baik:

type User { name: String }

2. Strukturkan Query Root Field secara Topikal

Kelompokkan entity utama di root query agar mudah ditemukan client.

type Query {
  user(id: ID!): User
  post(id: ID!): Post
  search(query: String!): [SearchResult!]
}

3. Gunakan Scalar dan Custom Scalar Separuh Pakai

Definisikan scalar customized seperti DateTime atau Email untuk validasi yang lebih baik.

scalar DateTime

type User {
  birthDate: DateTime
}

4. Manfaatkan Input Type pada Mutasi

Alih-alih menaruh banyak argumen pada fungsi mutasi, gunakan input object.

input CreatePostInput {
  title: String!
  content: String!
}

type Mutation {
  createPost(input: CreatePostInput!): Post!
}

5. Pakai Non-Null (!) Jika Data Harus Ada

Bantu client developer dengan menandai field yang “wajib” (!).

type User {
  id: ID!
  email: String!
  phone: String
}

6. Dukung Pagination dan Batasi Query yang Over-fetching

Gunakan pola relay (edges & pageInfo) atau cukup limit, offset sederhana.

type Query {
  posts(first: Int, after: String): PostConnection!
}

7. Adopsi Standard Connection Pattern untuk Pagination

Bentuknya seperti ini:

type PostConnection {
  pageInfo: PageInfo!
  edges: [PostEdge!]
}

type PostEdge {
  node: Post!
  cursor: String!
}

Ini membantu konsistensi dan alat built-in GraphQL bekerja maksimal.


8. Imposisikan Authorization di Level Skema

Definisikan custom directive agar developer tahu mana field/operation yang secured (bekerja sama dengan backend auth logic).

directive @auth(role: Role!) on FIELD_DEFINITION

type Query {
  me: User! @auth(role: USER)
  adminPanel: Panel! @auth(role: ADMIN)
}

9. Ekspos Error Secara Terstruktur

Gunakan pola union/error payloads.

union CreateUserResult = User | ValidationError

type Mutation {
  createUser(input: CreateUserInput!): CreateUserResult!
}

10. Jangan Over-Nest! Hindari Skema yang Terlalu Dalam

Terlalu banyak level membuat query sulit di-maintain dan riskan N+1 masalah.

# Hindari kedalaman relasi tak terkontrol
query {
  user {
    posts {
      comments {
        author {
          posts { ... }
        }
      }
    }
  }
}

Terapkan depth limit di backend.


11. Tambah Simulasi Rate Limiting via Directive

directive @rateLimit(max: Int, window: String) on FIELD_DEFINITION

type Query {
  heavyResource: Data! @rateLimit(max: 5, window: "1m")
}

12. Batasi Query Complexity di Layer Server

Implementasi instrumentasi untuk hitung “bobot” tiap field. Misal mutation dengan efek berat diberi skor tinggi.


13. Komentari Schema dengan Deskripsi

Deskripsi bermanfaat untuk dokumentasi otomatis seperti GraphQL Playground atau GraphiQL.

"""
User is a person that uses the app
"""
type User {
  ...
}

14. Pisahkan Mutation untuk Create, Update, Delete

Jangan buat satu mutation “savePost”. Dipisah demi kejelasan.

type Mutation {
  createPost(...): Post!
  updatePost(...): Post!
  deletePost(id: ID!): Boolean!
}

15. Avoid Breaking Changes—Versioning Bijak

Hindari modifikasi/removal field/type secara tiba-tiba. Gunakan deprecated.

type User {
  oldField: String @deprecated(reason: "Use newField instead")
  newField: String
}

16. Optimalkan untuk Batching & Caching

Aplikasi DataLoader di backend untuk batching, cache resolusi child node.


17. Pakai Enum dan Union dengan Bijak

Enum untuk nilai terbatas.

enum OrderStatus {
  PENDING
  PROCESSED
  DELIVERED
}

Union untuk multi tipe hasil query.


18. Gunakan Fragments untuk Konsistensi Query Client

Di sisi client, fragment membantu hindari duplikasi struktur query.

fragment userInfo on User {
  id
  name
  avatar
}

19. Tambahkan Field Metadata pada Edge-case

Seperti totalCount untuk pagination, hasMore untuk lazy load, dst.

type PageInfo {
  hasNextPage: Boolean!
  totalCount: Int
}

20. Audit Security: Limit Introspection di Production

Introspection idealnya hanya aktif saat development.


21. Rawat Skema dengan Tooling CI/CD

Lint dan validate skema secara otomatis (misal pakai graphql-schema-linter), deploy preview, behavioral test di pipeline CI.


Simulasi Kasus: Alur CreateUser

Mari lihat skema, alur logic, dan error handling:

input CreateUserInput {
  name: String!
  email: String!
  password: String!
}

type ValidationError {
  field: String!
  message: String!
}

union CreateUserResult = User | ValidationError

type Mutation {
  createUser(input: CreateUserInput!): CreateUserResult!
}

Contoh Resolver Pseudocode

async function createUser(parent, args, context) {
  const { name, email, password } = args.input
  const errors = validateUser(args.input)
  if (errors.length > 0) {
    return { field: errors[0].field, message: errors[0].message }
  }
  const user = await db.user.create({ name, email, passwordHash: hash(password) })
  return user
}

Visualisasi: Diagram Alur CreateUser

graph TD
  A[Client kirim createUser] --> B{Validasi Input}
  B -- Tidak valid --> C[Return ValidationError]
  B -- Valid --> D[Simpan ke Database]
  D --> E[Return User]

Tabel Checklist Best Practice

PraktikManfaat
Konsistensi namingMudah dipahami seluruh tim
Custom scalarValidasi & tipe data lebih baik
InputType untuk mutationMudah scaling & maintain param
Pagination standarKonsistensi pattern konsumsi data
Authorization directiveJelas mana yang butuh otentikasi
Field Non-nullClient aware data harus ada
CI/CD linterCegah bug & breaking change
Enum & unionTipe aman, query powerful
Dokumentasi skemaOtomatisasi API doc

Penutup

Merancang skema GraphQL yang solid itu ibarat fondasi rumah digital kita. Dengan menerapkan 21 best practice di atas, Anda dan tim dapat membangun API yang scalable, mudah dipelihara, aman, dan disukai oleh developer client maupun frontend. Tidak ada satu solusi yang mutlak: terus evaluasi, adaptasi, dan belajar dari kebutuhan bisnis serta feedback tim konsumsi API.

Jika punya pengalaman unik atau tips lain, bagikan di kolom komentar!
Happy GraphQL-ing 🚀


References:

comments powered by Disqus

Topik Terhangat

programming
221
tutorial
88
tips-and-trick
43
jaringan
28
hardware
11
linux
4
kubernetes
1