27 Menulis Skema Mutation di GraphQL
Sejak GraphQL menjadi primadona dalam arsitektur API, berbagai istilah seperti query, mutation, dan subscription mulai akrab di telinga developer. Sementara query digunakan untuk membaca data, mutation adalah elemen krusial untuk menulis atau memodifikasi data (insert, update, delete). Menulis skema mutation yang baik tidak sekadar mengikuti sintaks, tapi juga mengutamakan robust-ness, kemudahan scaling, dan maintainability.
Artikel ini berisi 27 tips praktis (best practice) menulis skema mutation di GraphQL, lengkap dengan contoh kode, simulasi, dan diagram alur yang bisa diterapkan di codebase Anda hari ini juga.
1. Pahami Fungsi Mutation
Mutation digunakan untuk semua operasi write pada data. Secara default, response dari mutation GraphQL adalah data terakhir setelah perubahan—unik dibanding REST.
1mutation {
2 createUser(input: {name: "Budi", email: "budi@example.com"}) {
3 id
4 name
5 email
6 }
7}2. Gunakan Input Object Types
Lebih baik gunakan input dibandingkan banyak argument langsung pada mutation, sehingga perubahan schema lebih terstruktur dan extendable.
1input CreateUserInput {
2 name: String!
3 email: String!
4}
5
6type Mutation {
7 createUser(input: CreateUserInput!): User
8}3. Selalu Return Payload Object
Usahakan mutation selalu mengembalikan object payload, bukan hanya satu field.
1type CreateUserPayload {
2 user: User
3 errors: [Error!]
4}
5
6type Mutation {
7 createUser(input: CreateUserInput!): CreateUserPayload
8}4. Return Data yang Sudah Diubah
Return-lah resource yang memang berubah agar frontend mudah melakukan cache update tanpa query kedua.
5. Dukung Bulk Operation (Batching)
Sediakan endpoint untuk batch create, update, dan delete untuk operasional skala besar.
1input BulkDeleteUserInput {
2 ids: [ID!]!
3}
4
5type Mutation {
6 bulkDeleteUser(input: BulkDeleteUserInput!): BulkDeletePayload
7}6. Semi-Standardisasi Nama Mutation
Gunakan pola verbNoun, misal: createUser, updateProfile, deleteComment.
7. Masukkan Informasi Error dan Success States
Contoh payload:
1type RegisterUserPayload {
2 user: User
3 success: Boolean!
4 errors: [Error!]
5}
6
7type Error {
8 field: String
9 message: String
10}8. Implementasikan Enumeration untuk State Tertentu
Hindari magic string. Gunakan enum untuk status, misal UserStatus.
9. Dokumentasikan Skema Mutation
Selalu tambahkan docstring pada setiap input dan type. Ini membantu tim dan tools documentation.
1"""
2Mendaftarkan user baru dengan email dan nama.
3"""
4createUser(input: CreateUserInput!): CreateUserPayload10. Gunakan Directive @deprecated
Jika ada mutation baru yang menggantikan yang lama, gunakan @deprecated.
1deletePostOld(input: DeletePostInput!): DeletePostPayload @deprecated(reason: "Use deletePost instead")11. Optimalkan Partial Update dengan Input Nullable
Untuk update, gunakan input nullable agar bisa partial update, mirip PATCH di REST.
1input UpdateUserInput {
2 id: ID!
3 name: String
4 email: String
5}12. Transaksi Atomic
Di backend resolver, pastikan mutation berjalan atomic (misal dengan database transaction).
13. Chained Mutation (Relay-style)
Setiap mutation bisa mengembalikan clientMutationId agar client bisa track response dan urutan operation.
1type Mutation {
2 updateProfile(input: UpdateProfileInput!): UpdateProfilePayload
3}
4input UpdateProfileInput {
5 clientMutationId: String
6 ...
7}
8type UpdateProfilePayload {
9 clientMutationId: String
10 ...
11}14. Respon Kondisional
Support field di payload untuk memberikan status keberhasilan atau error spesifik.
15. Validasi Input di Resolver
Selalu lakukan input validation di layer resolver, walaupun sudah ada GraphQL validation di level schema.
16. Use-case Driven Mutation Granularity
Jangan terlalu granuler atau terlalu besar. Misal, update satu profile field saja? Atau sekalian update semua field sekalian? Sesuaikan dengan use-case frontend.
17. Protected Action (Authentication dan Authorization)
Mutation harus aman, implementasikan guard/middleware sesuai kebutuhan.
18. Jangan Gunakan Mutation untuk Query-only
Mutation sebaiknya tidak untuk operasi read saja.
19. Exposed Field Secara Selektif
Jangan expose semua field, misal field password hash tetap hidden.
20. Idempotency
Desain mutation agar idempotent, apalagi untuk sensitive operation (transfer uang, dll).
21. Error Mapping yang Konsisten
Error harus punya pola response yang sama di seluruh mutation.
22. Test Mutation Terus-menerus
Mutasi = write = rawan bug. Unit/integration test wajib.
23. Buat Enum MutationStatus Jika Dibutuhkan
Response mutation bisa punya enum: SUCCESS, FAILED, DUPLICATE, dsb.
24. Beri Tahu Client Saat Ada Side-effect
Contoh: user perlu verifikasi email, info-kan di payload.
25. Simulasi: Update User Profile
Mari simulasikan:
Skema:
1input UpdateProfileInput {
2 id: ID!
3 name: String
4 email: String
5 bio: String
6}
7type UpdateProfilePayload {
8 user: User
9 errors: [Error!]
10 success: Boolean!
11}
12type Mutation {
13 updateProfile(input: UpdateProfileInput!): UpdateProfilePayload
14}Resolver (pseudocode Express.js/TypeScript):
1const resolvers = {
2 Mutation: {
3 updateProfile: async (_, {input}, {db, user}) => {
4 if (!user) return { success: false, errors: [{message: 'Unauthorized'}] };
5
6 const profile = await db.User.findById(input.id);
7 if (!profile) return { success: false, errors: [{message: 'Not found'}] };
8
9 if (input.email && !isValidEmail(input.email)) {
10 return { success: false, errors: [{message: 'Invalid email'}] };
11 }
12
13 Object.assign(profile, input);
14
15 await profile.save();
16
17 return { user: profile, success: true, errors: [] };
18 }
19 }
20}Query:
1mutation {
2 updateProfile(input: {
3 id: "12",
4 name: "Budi Santoso",
5 email: "budi@contoh.com"
6 }) {
7 user {
8 id
9 name
10 email
11 }
12 success
13 errors {
14 message
15 }
16 }
17}26. Tabel Perbandingan Schema: REST vs GraphQL Mutation
| REST | GraphQL | |
|---|---|---|
| Endpoint | /users/{id} | mutation {updateUser} |
| HTTP Verb | PATCH | Custom via schema |
| Payload | JSON body | Param via input object |
| Response | Tergantung implementasi | Custom payload, bisa error & data |
| Validation | Manual, biasanya via middleware | Schema + custom resolver validation |
| Atomicity | Tergantung backend | Tergantung resolver |
| Versi/Depend | Perlu versioning API | Skema backward compatible & deprecation |
27. Diagram Alur Mutation GraphQL
flowchart TD
A[Client] -->|Send Mutation Payload| B[GraphQL Server]
B --> C{Validation}
C -- valid --> D[Trigger Resolver]
D --> E[DB Transaction]
E --> F[Updated Data]
F --> G[Return Payload (User, Errors, Success, etc)]
C -- invalid --> H[Return Error Payload]
Kesimpulan
Menulis 27 skema mutation terbaik di GraphQL bukan hanya soal sintaks, melainkan memadukan aspek fungsional, keamanan, kemudahan maintain, serta scalability. Jadikanlah mutation di API Anda sebagai kontrak yang jelas antara client <> server, dan jangan ragu refactor untuk adaptasi kebutuhan baru.
Selamat bereksplorasi dengan mutation GraphQL—karena data yang berubah, membawa aplikasi bertumbuh! 🚀
Referensi