GraphQL adalah cara lain meminta data dari API: client menulis bentuk data yang dia mau, dan server menjawab persis bentuk itu, lewat satu endpoint (biasanya /graphql). Bayangin warung yang cuma jual paket. Pesan "paket nasi goreng" selalu dapat nasi, telur, kerupuk, dan acar, walaupun kamu cuma mau nasi dan telur. Mau es teh? Antre lagi di meja minuman. GraphQL itu formulir pesanan: kamu centang yang kamu mau, kirim sekali, dan yang datang persis yang dicentang.
🧪 Tebakan cepat: kamu minta
book(id: 99), padahal buku 99 gak ada. Status HTTP berapa yang kamu terima? Tebak dulu, jawabannya ada di bagian "Error datang dengan status 200".
Masalah REST yang mau diselesaikan
Layar daftar buku di aplikasi Toko Buku cuma butuh judul buku dan nama penulisnya. Dengan REST, client melakukan ini:
| Request | Yang datang | Yang dipakai |
|---|---|---|
GET /books/1 |
id, title, author_id, price, stock |
title saja |
GET /authors/1 |
id, name |
name saja |
Dua masalah sekaligus:
- Over-fetching: endpoint mengirim lebih banyak field dari yang dibutuhkan.
GET /books/1mengirim 5 field, yang dipakai 1. - Under-fetching: satu endpoint gak cukup, jadi client harus request lagi. Nama penulis butuh request kedua.
Untuk 3 buku, layar itu butuh 1 request daftar buku plus 3 request penulis: 4 request.
Query pertamamu
Dengan GraphQL, client menulis query, operasi untuk membaca data:
query {
book(id: 1) {
title
price
}
}
Jawabannya:
{ "data": { "book": { "title": "Laskar Pelangi", "price": 89000 } } }
querymenandai operasi baca.book(id: 1)adalah field dengan argumenid: 1: buku yang mana.{ title price }adalah selection set: field yang kamu minta. Cuma itu yang dikirim.- Jawabannya selalu dibungkus
data, dan bentuknya mengikuti bentuk query.
Relasi dalam satu request
Field bisa bersarang. Penulis buku diminta di dalam buku itu:
query {
book(id: 1) {
title
author {
name
}
}
}
Jawabannya {"data": {"book": {"title": "Laskar Pelangi", "author": {"name": "Andrea Hirata"}}}}. Satu request, dua "tabel", tanpa field yang gak dipakai. Over-fetching dan under-fetching hilang. Field yang isinya objek (author) wajib punya selection set sendiri; field biasa (title) gak boleh punya.
Schema: daftar yang boleh diminta
Server GraphQL punya schema, daftar type dan field yang boleh diminta:
type Query {
book(id: Int!): Book
books: [Book!]!
}
type Book {
id: Int!
title: String!
price: Int!
stock: Int!
author: Author!
}
type Author {
id: Int!
name: String!
books: [Book!]!
}
Int!artinya angka bulat yang gak bolehnull.Booktanpa!artinya bolehnull(bukunya bisa gak ada).[Book!]!artinya daftar buku, gak pernahnull.
Minta field yang gak ada di schema, misalnya isbn, dan query ditolak sebelum jalan dengan pesan Cannot query field "isbn" on type "Book". Banyak server (Apollo, misalnya) menjawabnya dengan 400 tanpa data.
Mutation: mengubah data
Operasi yang mengubah data (create, update, delete) namanya mutation. Bentuknya sama dengan query, cuma kata kuncinya beda:
mutation {
createBook(title: "Ronggeng Dukuh Paruk", authorId: 4, price: 95000) {
id
title
}
}
Argumen membawa datanya, dan selection set memilih apa yang mau dibaca balik dari buku barunya: {"data": {"createBook": {"id": 4, "title": "Ronggeng Dukuh Paruk"}}}.
Latihan memakai schema blog, polanya sama persis. User nomor 1 dengan nama dan judul tulisannya, lalu user baru, dalam satu dokumen:
query AmbilUser { user(id: 1) { name posts { title } } }
mutation BuatUser { createUser(name: "Andi", email: "[email protected]") { id name email } }
Satu dokumen yang berisi dua operasi wajib memberi nama keduanya (AmbilUser, BuatUser), supaya server tahu mana yang dijalankan. Tanpa nama, dokumennya ditolak dengan This anonymous operation must be the only defined operation.
Memanggil GraphQL dari JavaScript
Semua operasi masuk ke satu URL dengan POST. Query-nya dikirim sebagai teks di dalam body JSON:
const res = await fetch("https://api.tokobuku.test/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "query { book(id: 1) { title price } }" }),
});
const { data, errors } = await res.json();
- Endpoint-nya cuma satu. Bedanya ada di isi
query, bukan di URL atau method. - Ambil
datadanerrorsdari body. Bagian berikut menjelaskan kenapa.
Error datang dengan status 200
Query book(id: 99) lolos validasi (field dan argumennya benar), lalu server mencari dan gak ketemu. Jawabannya tetap 200 OK, dan kabar buruknya ada di body:
{ "errors": [{ "message": "Book not found", "path": ["book"] }], "data": { "book": null } }
Itu jawaban tebakannya: 200. Di REST, GET /books/99 menjawab 404. Di GraphQL, satu request bisa berisi beberapa field, dan sebagian bisa berhasil sementara yang lain gagal, jadi status HTTP gak bisa mewakili semuanya. path menunjuk field yang gagal. Karena itu client wajib mengecek errors, bukan cuma status.
🤔 Kapan GraphQL, kapan REST
- GraphQL cocok saat banyak layar butuh bentuk data yang berbeda-beda dari relasi yang sama, misalnya aplikasi mobile yang hemat kuota.
- REST cocok untuk API sederhana dan API publik.
GETke URL yang berbeda gampang di-cache browser dan CDN; GraphQL biasanyaPOSTke satu URL, jadi cache HTTP gak bisa memakainya begitu saja. - Kebebasan client ada harganya:
books { author { name } }di server bisa jadi 1 query daftar buku plus 1 query penulis per buku (masalah N+1), dan query yang bersarang sangat dalam bisa memberatkan server. Ada juga operasi ketiga, subscription, untuk data real-time. Semua itu dibahas di track GraphQL, mulai dari "Apa itu GraphQL?" sampai "N+1 Problem & DataLoader".
⚠️ Jebakan yang sering ketemu
- Field objek tanpa selection set.
authorataupostssendirian ditolak. Tulisposts { title }. - Cuma mengecek status. Status
200belum tentu berhasil. Cekerrorsdi body. - Pakai
queryuntuk mengubah data. Create, update, dan delete ditulis sebagaimutation. - Komentar pakai
//. Di GraphQL, komentar ditulis dengan#.
TL;DR: GraphQL memakai satu endpoint (POST /graphql); client menulis bentuk data yang dia mau, jadi gak ada over-fetching (field berlebih) dan under-fetching (request tambahan). query membaca, mutation mengubah data. book(id: 1) { title author { name } }: field, argumen, dan selection set yang bisa bersarang; field objek wajib punya selection set. Schema menentukan apa yang boleh diminta; field yang gak ada ditolak sebelum jalan. Error saat menjalankan query datang dengan status 200 dan array errors, jadi selalu cek errors.