Pelajari apa itu GraphQL, cara kerja query, mutation, dan skema, serta kapan menggunakannya daripada REST—termasuk kelebihan, kekurangan, dan contoh praktis.

GraphQL adalah bahasa query dan runtime untuk API. Singkatnya: ini cara bagi sebuah aplikasi (web, mobile, atau layanan lain) untuk meminta data dari API menggunakan permintaan yang jelas dan terstruktur—dan agar server mengembalikan respons yang sesuai dengan permintaan itu.
Banyak API memaksa klien menerima apa pun yang dikembalikan endpoint tetap. Itu sering menyebabkan dua masalah:
Dengan GraphQL, klien dapat meminta tepat field yang dibutuhkannya, tidak lebih dan tidak kurang. Ini sangat berguna ketika layar berbeda (atau aplikasi berbeda) membutuhkan “potongan” data yang berbeda dari data yang sama.
GraphQL biasanya berada di antara aplikasi klien dan sumber data Anda. Sumber data tersebut bisa berupa:
Server GraphQL menerima sebuah query, menentukan bagaimana mengambil setiap field yang diminta dari tempat yang tepat, lalu merakit respons JSON akhir.
Pikirkan GraphQL sebagai memesan respons berbentuk khusus:
GraphQL sering disalahpahami, jadi beberapa klarifikasi:
Jika Anda mempertahankan definisi inti itu—bahasa query + runtime untuk API—Anda akan memiliki fondasi yang tepat untuk semua hal lainnya.
GraphQL dibuat untuk memecahkan masalah produk yang praktis: tim menghabiskan terlalu banyak waktu membuat API cocok dengan layar UI.
API tradisional berbasis endpoint sering memaksa pilihan antara mengirim data yang tidak Anda butuhkan atau membuat panggilan tambahan untuk mendapatkan apa yang Anda butuhkan. Saat produk tumbuh, gesekan itu muncul sebagai halaman lebih lambat, kode klien yang lebih rumit, dan koordinasi yang menyakitkan antara tim frontend dan backend.
Over-fetching terjadi ketika sebuah endpoint mengembalikan objek “lengkap” meskipun sebuah layar hanya membutuhkan beberapa field. Tampilan profil mobile mungkin hanya perlu nama dan avatar, tetapi API mengembalikan alamat, preferensi, field audit, dan lainnya. Itu memborong bandwidth dan dapat merusak pengalaman pengguna.
Under-fetching adalah kebalikan: tidak ada endpoint tunggal yang memiliki semua yang dibutuhkan tampilan, jadi klien harus membuat banyak permintaan dan menyambungkan hasilnya. Itu menambah latensi dan meningkatkan peluang kegagalan parsial.
Banyak API gaya REST merespons perubahan dengan menambah endpoint baru atau versioning (v1, v2, v3). Versioning bisa diperlukan, tetapi menciptakan pekerjaan pemeliharaan jangka panjang: klien lama terus menggunakan versi lama, sementara fitur baru menumpuk di tempat lain.
Pendekatan GraphQL adalah mengembangkan skema dengan menambahkan field dan tipe dari waktu ke waktu, sambil menjaga field yang ada tetap stabil. Itu sering mengurangi tekanan untuk membuat “versi baru” hanya untuk mendukung kebutuhan UI baru.
Produk modern jarang hanya memiliki satu konsumen. Web, iOS, Android, dan integrasi mitra semua membutuhkan bentuk data yang berbeda.
GraphQL dirancang agar setiap klien dapat meminta tepat field yang dibutuhkannya—tanpa backend membuat endpoint terpisah untuk setiap layar atau perangkat.
API GraphQL didefinisikan oleh skema. Anggap itu sebagai perjanjian antara server dan setiap klien: mencantumkan data apa yang ada, bagaimana data saling terhubung, dan apa yang bisa diminta atau diubah. Klien tidak menebak endpoint—mereka membaca skema dan meminta field tertentu.
Skema terdiri dari tipe (seperti User atau Post) dan field (seperti name atau title). Field bisa merujuk ke tipe lain, itulah cara GraphQL memodelkan relasi.
Berikut contoh sederhana dalam Schema Definition Language (SDL):
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String
author: User!
comments: [Comment!]!
}
type Comment {
id: ID!
text: String!
author: User!
post: Post!
}
Karena skema bertipes kuat, GraphQL dapat memvalidasi permintaan sebelum menjalankannya. Jika klien meminta field yang tidak ada (misalnya, Post.publishDate saat skema tidak memiliki field itu), server dapat menolak atau memenuhi sebagian permintaan dengan error yang jelas—tanpa perilaku ambigu “mungkin berhasil”.
Skema dirancang untuk tumbuh. Anda biasanya bisa menambahkan field baru (mis. User.bio) tanpa memecah klien yang ada, karena klien hanya menerima apa yang mereka minta. Menghapus atau mengubah field lebih sensitif, jadi tim sering menandai field sebagai deprecated terlebih dahulu dan memigrasikan klien secara bertahap.
API GraphQL biasanya diekspos melalui satu endpoint (mis. /graphql). Alih-alih memiliki banyak URL untuk sumber daya berbeda (seperti /users, /users/123, /users/123/posts), Anda mengirim query ke satu tempat dan menggambarkan data persis yang Anda inginkan kembali.
Query pada dasarnya adalah “daftar belanja” field. Anda dapat meminta field sederhana (seperti id dan name) dan juga data bersarang (seperti posting terbaru seorang user) dalam satu permintaan—tanpa mengunduh field tambahan yang tidak Anda butuhkan.
Berikut contoh kecil:
query GetUserWithPosts {
user(id: "123") {
id
name
posts(limit: 2) {
id
title
}
}
}
Respons GraphQL dapat diprediksi: JSON yang Anda terima mencerminkan struktur query Anda. Itu membuatnya lebih mudah digunakan di frontend, karena Anda tidak perlu menebak di mana data akan muncul atau mengurai format respons yang berbeda.
Garis besar respons sederhana mungkin terlihat seperti:
{
"data": {
"user": {
"id": "123",
"name": "Sam",
"posts": [
{ "id": "p1", "title": "Hello GraphQL" },
{ "id": "p2", "title": "Queries in Practice" }
]
}
}
}
Jika Anda tidak meminta sebuah field, field itu tidak akan disertakan. Jika Anda memintanya, Anda dapat mengharapkannya di tempat yang sesuai—membuat query GraphQL menjadi cara yang rapi untuk mengambil persis apa yang diperlukan setiap layar atau fitur.
Query untuk membaca; mutations adalah cara Anda mengubah data dalam API GraphQL—membuat, memperbarui, atau menghapus record.
Sebagian besar mutation mengikuti pola yang sama:
input) seperti field yang akan diupdate.\n2. Validasi & otorisasi: server memeriksa hal seperti field wajib, format data, keunikan, dan apakah pengguna diizinkan melakukan tindakan itu.\n3. Tulis: server melakukan perubahan pada basis data (atau memanggil layanan lain).\n4. Payload/tipe pengembalian: server mengembalikan bentuk hasil yang dapat diprediksi sehingga UI dapat diperbarui.Mutation GraphQL biasanya mengembalikan data dengan sengaja, bukannya hanya “success: true”. Mengembalikan objek yang diperbarui (atau setidaknya id dan field kunci) membantu UI:\n
Desain umum adalah tipe “payload” yang mencakup entitas yang diperbarui dan setiap error.
mutation UpdateEmail($input: UpdateUserEmailInput!) {
updateUserEmail(input: $input) {
user {
id
email
}
errors {
field
message
}
}
}
Untuk API yang digerakkan UI, aturan yang baik adalah: kembalikan apa yang diperlukan untuk merender status berikutnya (mis. user yang diperbarui plus errors). Itu membuat klien sederhana, menghindari menebak apa yang berubah, dan membuat kegagalan lebih mudah ditangani secara elegan.
Skema GraphQL menjelaskan apa yang bisa diminta. Resolver menjelaskan bagaimana sebenarnya mendapatkannya. Resolver adalah fungsi yang terikat ke field tertentu dalam skema Anda. Ketika klien meminta field itu, GraphQL memanggil resolver untuk mengambil atau menghitung nilai.
GraphQL mengeksekusi query dengan menelusuri bentuk yang diminta. Untuk setiap field, ia menemukan resolver yang cocok dan menjalankannya. Beberapa resolver hanya mengembalikan properti dari objek yang sudah ada di memori; yang lain memanggil basis data, layanan lain, atau menggabungkan beberapa sumber.
Misalnya, jika skema Anda memiliki User.posts, resolver posts mungkin melakukan query ke tabel posts berdasarkan userId, atau memanggil layanan Posts terpisah.
Resolver adalah lem yang menghubungkan skema dengan sistem nyata Anda:
Pemetaan ini fleksibel: Anda dapat mengubah implementasi backend tanpa mengubah bentuk query klien—selama skema tetap konsisten.
Karena resolver dapat berjalan per field dan per item dalam daftar, mudah tidak sengaja memicu banyak panggilan kecil (mis. mengambil posts untuk 100 users dengan 100 query terpisah). Pola “N+1” ini dapat membuat respons menjadi lambat.
Perbaikan umum termasuk batching dan caching (mis. mengumpulkan ID dan mengambil dalam satu query) dan sengaja membatasi field bersarang yang Anda dorong klien untuk minta.
Otorisasi sering diterapkan di resolver (atau middleware bersama) karena resolver tahu siapa yang meminta (melalui context) dan data apa yang diakses. Validasi biasanya terjadi pada dua level: GraphQL menangani validasi tipe/bentuk secara otomatis, sementara resolver menegakkan aturan bisnis (mis. “hanya admin yang boleh mengatur field ini”).
Satu hal yang mengejutkan orang baru ke GraphQL adalah bahwa sebuah permintaan bisa “berhasil” dan tetap menyertakan error. Itu karena GraphQL berorientasi pada field: jika beberapa field dapat diselesaikan dan yang lain tidak, Anda mungkin mendapatkan data parsial kembali.
Respons GraphQL tipikal bisa berisi data dan array errors:
{
"data": {
"user": {
"id": "123",
"email": null
}
},
"errors": [
{
"message": "Not authorized to read email",
"path": ["user", "email"],
"extensions": { "code": "FORBIDDEN" }
}
]
}
Ini berguna: klien masih bisa merender apa yang ada (mis. profil user) sambil menangani field yang hilang.
data sering null.Tulis pesan error untuk pengguna akhir, bukan untuk debugging. Hindari mengekspos stack trace, nama basis data, atau ID internal. Pola yang baik adalah:
message)\n- extensions.code yang dapat dibaca mesin dan stabil\n- Metadata opsional yang aman (mis. retryable: true)Log error detail di sisi server dengan request ID sehingga Anda bisa menyelidiki tanpa mengekspose internal.
Definisikan “kontrak” error kecil yang dibagi web dan mobile: nilai extensions.code umum (seperti UNAUTHENTICATED, FORBIDDEN, BAD_USER_INPUT), kapan menampilkan toast vs error inline per-field, dan bagaimana menangani data parsial. Konsistensi di sini mencegah setiap klien menciptakan aturan error sendiri.
Subscriptions adalah cara GraphQL untuk mendorong data ke klien saat berubah, alih-alih membuat klien bertanya berulang kali. Mereka biasanya dikirim melalui koneksi persisten (paling umum WebSockets), sehingga server dapat mengirim event saat sesuatu terjadi.
Sebuah subscription mirip dengan query, tetapi hasilnya bukan respons tunggal. Ia adalah aliran hasil—masing-masing mewakili sebuah event.
Di bawah permukaan, klien “subscribe” ke topik (mis. messageAdded di aplikasi chat). Ketika server mem-publish event, pelanggan yang terhubung menerima payload yang cocok dengan selection set subscription.
Subscriptions unggul ketika pengguna mengharapkan perubahan instan:
Dengan polling, klien bertanya “Ada yang baru?” setiap N detik. Itu sederhana, tetapi dapat membuang-buang permintaan (terutama saat tidak ada perubahan) dan terasa tertunda.
Dengan subscriptions, server mengatakan “Inilah pembaruan” secara segera. Itu dapat mengurangi lalu lintas yang tidak perlu dan meningkatkan persepsi kecepatan—dengan biaya mempertahankan koneksi terbuka dan mengelola infrastruktur real-time.
Subscriptions tidak selalu sepadan. Jika pembaruan jarang, tidak sensitif waktu, atau mudah digabung, polling (atau sekadar fetch ulang setelah aksi pengguna) sering cukup.
Mereka juga menambah overhead operasional: penskalaan koneksi, otentikasi pada sesi jangka panjang, retry, dan monitoring. Aturan praktis: gunakan subscriptions hanya bila real-time adalah kebutuhan produk, bukan sekadar nice-to-have.
GraphQL sering digambarkan sebagai “kekuatan bagi klien”, tetapi kekuatan itu memiliki biaya. Mengetahui tradeoff di muka membantu memutuskan kapan GraphQL cocok—dan kapan mungkin berlebihan.
Kemenangan terbesar adalah fleksibilitas pengambilan data: klien dapat meminta tepat field yang dibutuhkan, yang dapat mengurangi over-fetching dan membuat perubahan UI lebih cepat.
Keunggulan besar lain adalah kontrak kuat yang diberikan oleh skema GraphQL. Skema menjadi sumber kebenaran tunggal untuk tipe dan operasi yang tersedia, yang meningkatkan kolaborasi dan tooling.
Tim sering melihat produktifitas klien yang lebih baik karena pengembang front-end dapat beriterasi tanpa menunggu variasi endpoint baru, dan alat seperti Apollo Client dapat menghasilkan tipe dan menyederhanakan pengambilan data.
GraphQL dapat membuat caching lebih kompleks. Dengan REST, caching sering “per URL.” Dengan GraphQL, banyak query berbagi endpoint yang sama, jadi caching bergantung pada bentuk query, cache ternormalisasi, dan konfigurasi server/klien yang hati-hati.
Di sisi server, ada jebakan performa. Query yang tampak kecil dapat memicu banyak panggilan backend kecuali Anda merancang resolver dengan cermat (batching, menghindari pola N+1, dan mengendalikan field mahal).
Ada juga kurva belajar: skema, resolver, dan pola klien bisa asing bagi tim yang terbiasa dengan API berbasis endpoint.
Karena klien dapat meminta banyak hal, API GraphQL harus menegakkan batas kedalaman dan kompleksitas query untuk mencegah permintaan yang abusif atau tidak sengaja “terlalu besar”.
Otentikasi dan otorisasi sebaiknya diterapkan per field, bukan hanya di level route, karena field berbeda mungkin memiliki aturan akses berbeda.
Secara operasional, investasikan pada logging, tracing, dan monitoring yang memahami GraphQL: lacak nama operasi, variabel (dengan hati-hati), waktu resolver, dan tingkat error sehingga Anda dapat mendeteksi query lambat dan regresi lebih awal.
GraphQL dan REST sama-sama membantu aplikasi berkomunikasi dengan server, tetapi mereka menyusun percakapan itu dengan cara yang sangat berbeda.
REST bersifat berbasis sumber daya. Anda mengambil data dengan memanggil banyak endpoint (URL) yang mewakili “benda” seperti /users/123 atau /orders?userId=123. Setiap endpoint mengembalikan bentuk data tetap yang ditentukan server.
REST juga bergantung pada semantik HTTP: metode seperti GET/POST/PUT/DELETE, kode status, dan aturan caching. Itu bisa membuat REST terasa alami ketika Anda melakukan CRUD sederhana atau bekerja dekat dengan cache browser/proxy.
GraphQL bersifat berbasis skema. Alih-alih banyak endpoint, Anda biasanya memiliki satu endpoint, dan klien mengirim query yang menggambarkan field persis yang diinginkan. Server memvalidasi permintaan itu terhadap skema GraphQL dan mengembalikan respons yang sesuai dengan bentuk query.
“Seleksi yang digerakkan klien” inilah yang membuat GraphQL dapat mengurangi over-fetching (terlalu banyak data) dan under-fetching (kekurangan data), terutama untuk layar UI yang membutuhkan data dari beberapa model terkait.
REST sering lebih cocok ketika:
Banyak tim mencampur keduanya:
Pertanyaan praktisnya bukan “Mana yang lebih baik?” tetapi “Mana yang sesuai pada kasus ini dengan kompleksitas paling sedikit?”
Mendesain API GraphQL paling mudah ketika Anda memperlakukannya sebagai produk untuk orang yang membangun layar, bukan sebagai cermin dari basis data Anda. Mulailah kecil, validasi dengan kasus penggunaan nyata, dan kembangkan sesuai kebutuhan.
Daftar layar kunci Anda (mis. “Daftar produk”, “Detail produk”, “Checkout”). Untuk setiap layar, tuliskan field tepat yang dibutuhkan dan interaksi yang didukung.
Ini membantu menghindari “query dewa”, mengurangi over-fetching, dan memperjelas di mana Anda membutuhkan filter, sorting, dan pagination.
Definisikan tipe inti Anda terlebih dahulu (mis. User, Product, Order) dan relasinya. Lalu tambahkan:
Gunakan penamaan yang berbicara bisnis daripada penamaan basis data. “placeOrder” menyampaikan maksud lebih baik daripada “createOrderRecord”.
Jaga konsistensi penamaan: tunggal untuk item (product), jamak untuk koleksi (products). Untuk pagination, biasanya Anda memilih salah satu:
Putuskan sejak dini karena itu membentuk struktur respons API Anda.
GraphQL mendukung deskripsi langsung di skema—gunakan untuk field, argumen, dan edge case. Lalu tambahkan beberapa contoh copy-paste di dokumentasi (termasuk pagination dan skenario error umum). Skema yang terdokumentasi baik membuat introspeksi dan explorer API jauh lebih berguna.
Memulai dengan GraphQL sebagian besar tentang memilih beberapa alat yang didukung baik dan menyiapkan alur kerja yang dapat diandalkan. Anda tidak perlu mengadopsi semuanya sekaligus—dapatkan satu query bekerja end-to-end, lalu kembangkan.
Pilih server berdasarkan stack Anda dan seberapa banyak “baterai terinklusi” yang Anda inginkan:
Langkah praktis pertama: definisikan skema kecil (beberapa tipe + satu query), implementasikan resolver, dan hubungkan sumber data nyata (meskipun hanya daftar in-memory yang di-stub).
Jika Anda ingin lebih cepat dari “ide” ke API yang bekerja, platform vibe-coding seperti Koder.ai dapat membantu Anda membuat scaffold aplikasi full-stack kecil (React di frontend, Go + PostgreSQL di backend) dan mengiterasi skema/resolver GraphQL via chat—lalu ekspor kode sumber ketika siap mengambil alih implementasinya.
Di frontend, pilihan biasanya bergantung apakah Anda ingin konvensi opini atau fleksibilitas:
Jika Anda bermigrasi dari REST, mulailah dengan menggunakan GraphQL untuk satu layar atau fitur, dan pertahankan REST untuk sisanya sampai pendekatan itu terbukti.
Perlakukan skema sebagai kontrak API. Lapisan pengujian yang berguna meliputi:
Untuk memperdalam pemahaman Anda, lanjutkan dengan:
GraphQL adalah bahasa query dan runtime untuk API. Klien mengirim query yang menggambarkan bidang (fields) yang diinginkan secara tepat, dan server mengembalikan respons JSON yang mencerminkan bentuk tersebut.
Ini paling baik dipahami sebagai lapisan di antara klien dan satu atau beberapa sumber data (basis data, layanan REST, API pihak ketiga, mikroservis).
GraphQL utamanya membantu mengatasi:
Dengan memungkinkan klien meminta field tertentu (termasuk bidang bersarang), GraphQL dapat mengurangi transfer data yang tidak perlu dan menyederhanakan kode klien.
GraphQL bukan:
Anggaplah sebagai kontrak API + mesin eksekusi, bukan solusi penyimpanan atau keajaiban performa.
Sebagian besar API GraphQL mengekspos satu endpoint (sering /graphql). Alih-alih banyak URL, Anda mengirim berbagai operasi (query/mutation) ke satu endpoint itu.
Implikasi praktis: caching dan observabilitas biasanya didasarkan pada nama operasi + variabel, bukan URL.
Skema adalah kontrak API. Ia mendefinisikan:
User, Post)\n- Field pada tipe tersebut (mis. User.name)\n- Relasi (mis. User.posts)Karena bertipe kuat, server dapat memvalidasi query sebelum mengeksekusinya dan memberikan error yang jelas saat field tidak ada.
Query GraphQL adalah operasi baca. Anda menentukan field yang dibutuhkan, dan JSON responsnya cocok dengan struktur query.
Tips:
query GetUserWithPosts) untuk debugging dan pemantauan yang lebih baik.\n- Gunakan argumen untuk membentuk hasil (mis. posts(limit: 2)).Mutations adalah operasi tulis (create/update/delete). Pola umum:
input\n- Validasi + otorisasi di server\n- Lakukan penulisan\n- Kembalikan payload yang mencakup data terupdate dan setiap errorMengembalikan data (bukan hanya success: true) membantu UI memperbarui tampilan segera dan menjaga konsistensi cache.
Resolver adalah fungsi tingkat-field yang memberi tahu GraphQL bagaimana mengambil atau menghitung setiap field.
Dalam praktik, resolver bisa:
Otorisasi sering diterapkan di resolver (atau middleware bersama) karena resolver tahu siapa yang meminta dan data apa yang diakses.
Mudah membuat pola N+1 (mis. memuat posts secara terpisah untuk tiap dari 100 users).
Mitigasi umum:
Ukur waktu eksekusi resolver dan perhatikan panggilan downstream yang berulang dalam satu permintaan.
GraphQL bisa mengembalikan data parsial bersamaan dengan array errors. Itu terjadi ketika beberapa field berhasil di-resolve dan yang lain gagal (mis. field terlarang, timeout layanan downstream).
Praktik baik:
message yang aman untuk pengguna\n- Tambahkan nilai extensions.code yang stabil (mis. FORBIDDEN, BAD_USER_INPUT)\n- Log detail error di sisi server dengan request IDKlien harus memutuskan kapan merender data parsial vs memperlakukan operasi sebagai kegagalan penuh.