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.
mutation {
  createUser(input: {name: "Budi", email: "budi@example.com"}) {
    id
    name
    email
  }
}
2. Gunakan Input Object Types
Lebih baik gunakan input dibandingkan banyak argument langsung pada mutation, sehingga perubahan schema lebih terstruktur dan extendable.
input CreateUserInput {
  name: String!
  email: String!
}
type Mutation {
  createUser(input: CreateUserInput!): User
}
3. Selalu Return Payload Object
Usahakan mutation selalu mengembalikan object payload, bukan hanya satu field.
type CreateUserPayload {
  user: User
  errors: [Error!]
}
type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload
}
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.
input BulkDeleteUserInput {
  ids: [ID!]!
}
type Mutation {
  bulkDeleteUser(input: BulkDeleteUserInput!): BulkDeletePayload
}
6. Semi-Standardisasi Nama Mutation
Gunakan pola verbNoun, misal: createUser, updateProfile, deleteComment.
7. Masukkan Informasi Error dan Success States
Contoh payload:
type RegisterUserPayload {
  user: User
  success: Boolean!
  errors: [Error!]
}
type Error {
  field: String
  message: String
}
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.
"""
Mendaftarkan user baru dengan email dan nama.
"""
createUser(input: CreateUserInput!): CreateUserPayload
10. Gunakan Directive @deprecated
Jika ada mutation baru yang menggantikan yang lama, gunakan @deprecated.
deletePostOld(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.
input UpdateUserInput {
  id: ID!
  name: String
  email: String
}
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.
type Mutation {
  updateProfile(input: UpdateProfileInput!): UpdateProfilePayload
}
input UpdateProfileInput {
  clientMutationId: String
  ...
}
type UpdateProfilePayload {
  clientMutationId: String
  ...
}
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:
input UpdateProfileInput {
  id: ID!
  name: String
  email: String
  bio: String
}
type UpdateProfilePayload {
  user: User
  errors: [Error!]
  success: Boolean!
}
type Mutation {
  updateProfile(input: UpdateProfileInput!): UpdateProfilePayload
}
Resolver (pseudocode Express.js/TypeScript):
const resolvers = {
  Mutation: {
    updateProfile: async (_, {input}, {db, user}) => {
      if (!user) return { success: false, errors: [{message: 'Unauthorized'}] };
      const profile = await db.User.findById(input.id);
      if (!profile) return { success: false, errors: [{message: 'Not found'}] };
      if (input.email && !isValidEmail(input.email)) {
        return { success: false, errors: [{message: 'Invalid email'}] };
      }
      Object.assign(profile, input);
      await profile.save();
      return { user: profile, success: true, errors: [] };
    }
  }
}
Query:
mutation {
  updateProfile(input: {
    id: "12", 
    name: "Budi Santoso", 
    email: "budi@contoh.com"
  }) {
    user {
      id
      name
      email
    }
    success
    errors {
      message
    }
  }
}
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
49 Serialisasi dan Deserialisasi Manual
Artikel Terhangat
111 Menambahkan Mutation ke Skema gqlgen
10 Oct 2025
108 Menangani Resolver Otomatis dan Manual
10 Oct 2025
106 Menulis Skema `.graphqls` untuk gqlgen
10 Oct 2025

111 Menambahkan Mutation ke Skema gqlgen

108 Menangani Resolver Otomatis dan Manual

