GraphQL adalah bahasa untuk menanyakan data ke sebuah API (query language), sekaligus mesin di server yang menjalankan pertanyaan itu. Client menulis field apa saja yang dia butuhkan, dan server menjawab dengan JSON yang bentuknya sama dengan pertanyaannya. Bayangin formulir di loket toko buku: kamu tulis "judul, harga, nama penulis" untuk buku nomor 1, petugas mengisi tiga kotak itu saja, dengan urutan yang sama, lalu mengembalikan formulirnya.
🧪 Tebakan cepat: Kamu minta field
isbn, padahal server gak punya field itu. Apa jawabannya:isbndiisinulldan field lain tetap dikirim, atau seluruh query ditolak? Jawabannya ada di bagian "Kalau ada yang salah: errors".
Masalahnya: halaman detail buku
Contoh di seluruh jalur GraphQL ini adalah Toko Buku, toko yang sama dengan di jalur API: tiga buku dan empat penulis.
| id | title | Penulis | price | stock |
|---|---|---|---|---|
| 1 | Laskar Pelangi | Andrea Hirata | 89000 | 12 |
| 2 | Bumi Manusia | Pramoedya Ananta Toer | 125000 | 0 |
| 3 | Cantik Itu Luka | Eka Kurniawan | 110000 | 5 |
Penulis keempat, Ahmad Tohari, belum punya buku di toko.
Halaman detail buku cuma butuh tiga hal: judul, harga, dan nama penulis. Dengan REST, client memanggil dua URL:
GET https://api.tokobuku.test/books/1
→ {"id":1,"title":"Laskar Pelangi","author_id":1,"price":89000,"stock":12} (72 byte)
GET https://api.tokobuku.test/authors/1
→ {"id":1,"name":"Andrea Hirata"} (31 byte)
- Over-fetching (data kebanyakan):
/books/1ikut mengirimid,author_id, danstock, padahal halaman ini gak memakainya. Bentuk response REST ditentukan server, sama untuk semua halaman. - Under-fetching (data kurang):
/books/1cuma memberiauthor_id, bukan nama penulis. Client harus menunggu response pertama, baru bisa minta/authors/1. Jadi dua kali bolak-balik ke server.
Totalnya 2 request berurutan, 103 byte diterima. Hitungan di sini cuma body JSON, tanpa header HTTP.
Query pertamamu
Dengan GraphQL, client menulis apa yang dibutuhkan dalam satu query:
query {
book(id: 1) {
title
price
author {
name
}
}
}
Server menjawab:
{
"data": {
"book": {
"title": "Laskar Pelangi",
"price": 89000,
"author": {
"name": "Andrea Hirata"
}
}
}
}
queryartinya operasi ini membaca data. Kata ini boleh dihilangkan:{ book(id: 1) { title } }artinya sama.book(id: 1)adalah sebuah field dengan argumentid: "buku yang id-nya 1".- Isi kurung kurawal setelahnya adalah field yang diminta dari buku itu.
author { name }masuk ke data penulisnya, jadi nama penulis ikut di response yang sama. - Bentuk response-nya mengikuti query:
bookberisititle,price, danauthor, dengan urutan yang sama. Gak adaid,stock, atauauthor_id, karena gak diminta.
Satu request, 92 byte diterima. Gak ada field yang berlebih, dan gak perlu request kedua.
Satu endpoint, dikirim lewat POST
API GraphQL biasanya cuma punya satu URL, misalnya https://api.tokobuku.test/graphql. Semua query dikirim ke situ. Yang membedakan tiap permintaan bukan URL-nya, tapi isi query-nya. Dari JavaScript, cukup pakai fetch biasa:
const response = await fetch("https://api.tokobuku.test/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "{ book(id: 1) { title price author { name } } }" }),
});
const result = await response.json();
console.log(result.data.book.author.name); // "Andrea Hirata"
- Method-nya
POST. Body-nya JSON dengan keyqueryyang berisi teks query-nya. Untuk query ini body-nya 59 byte. - Gak perlu library khusus. Library seperti Apollo Client atau urql cuma mempermudah hal yang sama.
- Hasilnya selalu objek JSON dengan key
data,errors, atau keduanya.
Kalau ada yang salah: errors
Response GraphQL punya dua tempat: data untuk hasilnya, errors untuk masalahnya. Coba minta field yang gak ada, misalnya isbn:
query {
book(id: 1) {
title
isbn
}
}
{
"errors": [
{
"message": "Cannot query field \"isbn\" on type \"Book\".",
"locations": [{ "line": 4, "column": 5 }]
}
]
}
Gak ada data sama sekali. Server memeriksa seluruh query sebelum menjalankannya, namanya validasi. Satu field yang gak dikenal sudah cukup bikin seluruh query ditolak, jadi title pun gak dikirim. Itu jawaban tebakannya. locations menunjuk baris 4 kolom 5, tempat isbn ditulis.
Beda lagi kalau query-nya valid tapi gagal waktu dijalankan, misalnya book(id: 99) padahal buku nomor 99 gak ada (query-nya sama dengan di atas, cuma id-nya 99 dan field-nya title saja):
{
"errors": [
{
"message": "Book not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["book"]
}
],
"data": {
"book": null
}
}
Sekarang data ada, isinya book: null, dan errors menjelaskan kenapa. path menunjuk field yang gagal. Pesan "Book not found" ditulis sendiri oleh server Toko Buku. Status HTTP untuk dua kasus ini dibahas di pelajaran berikutnya.
Yang menentukan field apa saja yang ada: schema
Server GraphQL punya schema: daftar type dan field yang boleh diminta, beserta type nilainya. Potongan schema Toko Buku:
type Query {
book(id: ID!): Book
}
type Book {
title: String!
price: Int!
author: Author!
}
Tiap field punya resolver, yaitu fungsi di server yang mencari nilainya, misalnya membaca dari database. Schema lengkapnya dibahas di pelajaran "Schema & Type System".
⚠️ Jebakan yang sering ketemu
- Menganggap GraphQL itu database. GraphQL gak menyimpan data. Dia lapisan di depan database, layanan lain, atau REST API yang sudah ada.
- Meminta object tanpa memilih field-nya.
authorsendirian ditolak dengan pesanField "author" of type "Author!" must have a selection of subfields. Did you mean "author { ... }"?. Object selalu butuh kurung kurawal berisi field. - Cuma mengecek status HTTP. Response yang berisi
errorsbisa datang dengan status200. Selalu cekerrorsdi body. - Mengira GraphQL pasti lebih hemat byte. Teks query-nya ikut dikirim (59 byte tadi). Yang pasti berkurang adalah jumlah request bolak-balik. Pelajaran berikutnya mengukurnya.
📌 Spesifikasinya
GraphQL dibuat di Facebook tahun 2012, lalu dibuka sebagai standar terbuka tahun 2015, dan sekarang dikelola GraphQL Foundation. Aturannya ditulis di spesifikasi di spec.graphql.org: rilis terbarunya edisi September 2025, sebelumnya edisi Oktober 2021, dan ada working draft untuk perubahan yang sedang dikerjakan. Spesifikasi itu sengaja gak mengatur HTTP. Cara mengirim GraphQL lewat HTTP ditulis di spesifikasi terpisah, GraphQL over HTTP, yang masih berstatus draft.
Server GraphQL ada di banyak bahasa: graphql-js (implementasi rujukan di JavaScript), Apollo Server, GraphQL Yoga, Lighthouse untuk Laravel, dan lainnya. Semuanya mengikuti spesifikasi yang sama, jadi bentuk query dan response-nya sama. Lab di halaman ini memakai mesin GraphQL kecil yang pesan error-nya disamakan dengan graphql-js 17.0.2.
TL;DR: GraphQL adalah query language untuk API: client menulis field yang dibutuhkan, server menjawab dengan JSON yang bentuknya sama, gak lebih dan gak kurang. Itu mengatasi over-fetching (data kebanyakan) dan under-fetching (data kurang sehingga butuh request tambahan): halaman detail buku turun dari 2 request REST (103 byte) jadi 1 query (92 byte). Semua query dikirim lewat POST ke satu endpoint, body-nya {"query": "..."}. Response-nya punya data, errors, atau keduanya: query yang gak valid ditolak utuh tanpa data, sedangkan field yang gagal waktu dijalankan jadi null dengan penjelasan di errors. Field apa saja yang ada ditentukan schema, dan nilainya dicari oleh resolver.