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:
1type Usr { nm: String }1type User { name: String }2. Strukturkan Query Root Field secara Topikal
Kelompokkan entity utama di root query agar mudah ditemukan client.
1type Query {
2 user(id: ID!): User
3 post(id: ID!): Post
4 search(query: String!): [SearchResult!]
5}3. Gunakan Scalar dan Custom Scalar Separuh Pakai
Definisikan scalar customized seperti DateTime atau Email untuk validasi yang lebih baik.
1scalar DateTime
2
3type User {
4 birthDate: DateTime
5}4. Manfaatkan Input Type pada Mutasi
Alih-alih menaruh banyak argumen pada fungsi mutasi, gunakan input object.
1input CreatePostInput {
2 title: String!
3 content: String!
4}
5
6type Mutation {
7 createPost(input: CreatePostInput!): Post!
8}5. Pakai Non-Null (!) Jika Data Harus Ada
Bantu client developer dengan menandai field yang “wajib” (!).
1type User {
2 id: ID!
3 email: String!
4 phone: String
5}6. Dukung Pagination dan Batasi Query yang Over-fetching
Gunakan pola relay (edges & pageInfo) atau cukup limit, offset sederhana.
1type Query {
2 posts(first: Int, after: String): PostConnection!
3}7. Adopsi Standard Connection Pattern untuk Pagination
Bentuknya seperti ini:
1type PostConnection {
2 pageInfo: PageInfo!
3 edges: [PostEdge!]
4}
5
6type PostEdge {
7 node: Post!
8 cursor: String!
9}8. Imposisikan Authorization di Level Skema
Definisikan custom directive agar developer tahu mana field/operation yang secured (bekerja sama dengan backend auth logic).
1directive @auth(role: Role!) on FIELD_DEFINITION
2
3type Query {
4 me: User! @auth(role: USER)
5 adminPanel: Panel! @auth(role: ADMIN)
6}9. Ekspos Error Secara Terstruktur
Gunakan pola union/error payloads.
1union CreateUserResult = User | ValidationError
2
3type Mutation {
4 createUser(input: CreateUserInput!): CreateUserResult!
5}10. Jangan Over-Nest! Hindari Skema yang Terlalu Dalam
Terlalu banyak level membuat query sulit di-maintain dan riskan N+1 masalah.
1# Hindari kedalaman relasi tak terkontrol
2query {
3 user {
4 posts {
5 comments {
6 author {
7 posts { ... }
8 }
9 }
10 }
11 }
12}11. Tambah Simulasi Rate Limiting via Directive
1directive @rateLimit(max: Int, window: String) on FIELD_DEFINITION
2
3type Query {
4 heavyResource: Data! @rateLimit(max: 5, window: "1m")
5}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.
1"""
2User is a person that uses the app
3"""
4type User {
5 ...
6}14. Pisahkan Mutation untuk Create, Update, Delete
Jangan buat satu mutation “savePost”. Dipisah demi kejelasan.
1type Mutation {
2 createPost(...): Post!
3 updatePost(...): Post!
4 deletePost(id: ID!): Boolean!
5}15. Avoid Breaking Changes—Versioning Bijak
Hindari modifikasi/removal field/type secara tiba-tiba. Gunakan deprecated.
1type User {
2 oldField: String @deprecated(reason: "Use newField instead")
3 newField: String
4}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.
1enum OrderStatus {
2 PENDING
3 PROCESSED
4 DELIVERED
5}18. Gunakan Fragments untuk Konsistensi Query Client
Di sisi client, fragment membantu hindari duplikasi struktur query.
1fragment userInfo on User {
2 id
3 name
4 avatar
5}19. Tambahkan Field Metadata pada Edge-case
Seperti totalCount untuk pagination, hasMore untuk lazy load, dst.
1type PageInfo {
2 hasNextPage: Boolean!
3 totalCount: Int
4}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:
1input CreateUserInput {
2 name: String!
3 email: String!
4 password: String!
5}
6
7type ValidationError {
8 field: String!
9 message: String!
10}
11
12union CreateUserResult = User | ValidationError
13
14type Mutation {
15 createUser(input: CreateUserInput!): CreateUserResult!
16}Contoh Resolver Pseudocode
1async function createUser(parent, args, context) {
2 const { name, email, password } = args.input
3 const errors = validateUser(args.input)
4 if (errors.length > 0) {
5 return { field: errors[0].field, message: errors[0].message }
6 }
7 const user = await db.user.create({ name, email, passwordHash: hash(password) })
8 return user
9}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
| Praktik | Manfaat |
|---|---|
| Konsistensi naming | Mudah dipahami seluruh tim |
| Custom scalar | Validasi & tipe data lebih baik |
| InputType untuk mutation | Mudah scaling & maintain param |
| Pagination standar | Konsistensi pattern konsumsi data |
| Authorization directive | Jelas mana yang butuh otentikasi |
| Field Non-null | Client aware data harus ada |
| CI/CD linter | Cegah bug & breaking change |
| Enum & union | Tipe aman, query powerful |
| Dokumentasi skema | Otomatisasi 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:
- GraphQL Official Best Practices
- Apollo GraphQL Patterns
- Pengalaman pribadi membangun skema API di startup Indonesia