Episode 5 membahas mutations untuk menulis data: struktur dan syntax dasar, perancangan input types yang baik, operasi CRUD lengkap, pola response dengan payload, eksekusi serial untuk multiple mutations, hingga best practices penamaan dan idempotensi.

Query memungkinkan kalian membaca data; mutation memungkinkan kalian mengubahnya. Episode 5 membedah mutation secara lengkap: struktur syntax, perancangan input type, operasi create-update-delete, dan pola response yang konsisten.
Kita juga membahas perilaku penting eksekusi serial untuk multiple mutations, serta best practices seperti penamaan, idempotensi, dan validasi di level input. Setelah episode ini, kalian bisa merancang lapisan tulis API GraphQL yang aman dan mudah dipakai.
Mutation ditulis dengan keyword mutation, diikuti operation name, dan seleksi field yang menjadi hasilnya:
mutation BuatPost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
createdAt
}
}Dengan variabel yang dikirim terpisah sebagai objek JSON {"input": {...}}:
{
"input": {
"title": "Belajar GraphQL",
"body": "Isi postingan pertama"
}
}Perbedaan kunci dengan query: mutation mengubah state dan dieksekusi secara serial. GraphQL menjamin field-field mutation dijalankan satu per satu, berurutan, sehingga mutation kedua dalam satu request melihat efek mutation pertama.
Selalu minta data hasil mutation pada response, bukan hanya status. Dengan mengembalikan objek yang dimutasi (seperti id, title, createdAt di atas), client tidak perlu melakukan query tambahan untuk mendapatkan data terbaru — menghilangkan round-trip ekstra.
Input type adalah cara GraphQL menerima data terstruktur untuk mutation. Prinsip desainnya: pindahkan validasi struktur ke level input, sehingga resolver tidak perlu menebak-nebak bentuk data:
input CreatePostInput {
title: String!
body: String!
tags: [String!]
publishedAt: DateTime
}Gunakan tipe yang spesifik, tandai field wajib dengan !, dan pisahkan input untuk create dan update seperti di episode 3. Jangan memakai object type untuk menerima data — object type tidak dirancang untuk itu.
Input yang dipakai banyak mutation (misalnya alamat atau kategori) bisa dijadikan satu input type yang dipakai ulang. Ini menjaga konsistensi dan mengurangi duplikasi. Perhatikan bahwa input type bisa bersarang — sebuah input bisa berisi input lain — untuk data yang bertingkat seperti item pesanan.
Pola umum untuk setiap entitas adalah empat mutation:
type Mutation {
createPost(input: CreatePostInput!): Post
updatePost(id: ID!, input: UpdatePostInput!): Post
deletePost(id: ID!): Post
}Untuk update, ada dua pendekatan. Full update mengharuskan seluruh field dikirim (cocok untuk form lengkap). Partial update memakai input dengan semua field nullable, seperti UpdatePostInput dengan title: String dan body: String, sehingga hanya field yang diberikan yang diubah — pola ini paling umum dipakai dan bersifat PATCH-style.
Untuk membuat atau memperbarui banyak entitas sekaligus, gunakan input bertipe list:
type Mutation {
createPosts(input: [CreatePostInput!]!): [Post]
}Pola ini mengurangi jumlah request dibanding memanggil mutation satu per satu. Perhatikan trade-off-nya: batch besar membuat request lama diproses dan error handling menjadi lebih kompleks, karena sebagian bisa sukses dan sebagian gagal.
Best practice modern adalah mengembalikan payload object yang membungkus hasil, alih-alih langsung mengembalikan objek:
type CreatePostResult {
post: Post
errors: [FieldError]
}
type FieldError {
field: String!
message: String!
}Dengan payload, kalian bisa mengembalikan data sukses sekaligus error per field dalam satu struktur. Ini menjadi dasar pola yang sangat powerful: result pattern dengan union types, yang akan kita bahas menyeluruh di episode 11. Untuk kesederhanaan awal, kalian bisa mulai dengan mengembalikan objek langsung, lalu berpindah ke payload saat API mulai kompleks.
Di sisi client, optimistic response memungkinkan UI memperbarui tampilan segera sebelum server menjawab, lalu disinkronkan saat response tiba. Ini bukan bagian dari schema, melainkan fitur client — kita akan mengimplementasikannya dengan Apollo Client di episode 25.
createX, updateX, deleteX, addTagToPost.createPost bukan submit.clientMutationId atau idempotency key.type Mutation {
createOrder(input: CreateOrderInput!, idempotencyKey: String!): Order
}Validasi di level input (episode 10) dan penanganan error yang konsisten (episode 11) adalah dua hal yang wajib ada pada setiap mutation di production.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kalian akan mempelajari resolvers — jantung GraphQL — mulai dari anatomy fungsi resolver, keempat parameter parent, args, context, dan info, resolver chains, context object, hingga pola async resolver dan organisasi file resolver. Inilah tempat data benar-benar diambil!