tutorial

27 Menulis Skema Mutation di GraphQL

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

RESTGraphQL
Endpoint/users/{id}mutation {updateUser}
HTTP VerbPATCHCustom via schema
PayloadJSON bodyParam via input object
ResponseTergantung implementasiCustom payload, bisa error & data
ValidationManual, biasanya via middlewareSchema + custom resolver validation
AtomicityTergantung backendTergantung resolver
Versi/DependPerlu versioning APISkema 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

comments powered by Disqus