GraphQL untuk Pemula: Konsep Query, Mutation, dan Subscription
Kalau kamu udah cukup lama berkecimpung di dunia web development, pasti nggak asing lagi dengan istilah REST API. Selama bertahun-tahun, REST jadi standar emas buat komunikasi antara frontend dan backend. Tapi, seiring waktu, mulai muncul beberapa “rasa sakit” yang bikin developer gerah — over-fetching, under-fetching, dan keharusan bikin banyak endpoint yang ujung-ujungnya susah di-maintain.
Nah, di sinilah GraphQL masuk ke cerita.
Dulu, waktu pertama kali dengar soal GraphQL, reaksi gue kurang lebih: “Ini apaan sih? Kok query-nya aneh, mirip JSON tapi bukan JSON?” Tapi begitu gue coba pakai buat project nyata, eureka — ternyata ini game changer banget, terutama buat project yang frontend-nya punya kebutuhan data yang dinamis.
Artikel ini gue tulis buat kamu yang mungkin punya perasaan yang sama kayak gue dulu. Kita bakal ngobrol santai tapi serius soal tiga konsep utama GraphQL: Query, Mutation, dan Subscription. Nggak perlu background mendalam di backend — cukup paham dasar API dan HTTP, kamu udah bisa ngikutin.
Apa Itu GraphQL dan Kenapa Harus Peduli?
GraphQL itu bukan database query language, ya. Meskipun namanya ada “QL”-nya, GraphQL adalah query language untuk API yang dikembangkan oleh Facebook (sekarang Meta) tahun 2012 dan dirilis ke publik tahun 2015. Intinya, GraphQL adalah sebuah spesifikasi — sebuah cara baru buat client dan server berkomunikasi soal data.
Bayangin kamu lagi di restoran. Kalau pakai REST, itu kayak kamu pesan “Paket A” — isinya udah ditentukan, mau suka atau nggak suka beberapa itemnya, ya udah terima aja. Kadang kebanyakan (over-fetching), kadang kurang (under-fetching) dan kamu harus pesan lagi.
Kalau pakai GraphQL? Kamu bisa bilang ke pelayan: “Gue mau nasi, ayam goreng tanpa sambal, dan es teh manis kurang gula.” Presisi. Nggak lebih, nggak kurang.
Keunggulan utama GraphQL:
- Single Endpoint — Nggak perlu bikin
/users,/users/:id/posts,/users/:id/commentssecara terpisah. Semua lewat satu URL aja, biasanya/graphql. - Client yang Tentukan Struktur Data — Frontend bilang butuh field apa aja, server ngasih sesuai permintaan.
- Strongly Typed — Semua data didefinisikan dalam schema, jadi error bisa ketangkep lebih awal.
- Introspection — Kamu bisa “tanya” ke API: “Eh, kamu bisa ngasih data apa aja sih?” Dan API bakal jawab dengan lengkap.
Cukup filosofinya. Sekarang kita masuk ke tiga konsep inti yang harus kamu pahami.
Query: Mengambil Data dari Server
Query adalah operasi paling dasar di GraphQL. Fungsinya simpel: mengambil data. Kalau di REST, ini setara dengan method GET.
Struktur Dasar Sebuah Query
Setiap query di GraphQL ditulis dalam bentuk yang sangat mirip dengan struktur JSON, tapi tanpa nilai-nilainya. Kamu cuma bilang: “Gue mau field apa aja.”
Contoh, kamu mau ambil data user:
query {
user(id: "123") {
name
email
avatar
}
}
Dan responsenya bakal persis seperti yang kamu minta:
{
"data": {
"user": {
"name": "Budi Santoso",
"email": "[email protected]",
"avatar": "https://cdn.example.com/budi.jpg"
}
}
}
Perhatikan: kamu cuma minta name, email, dan avatar. Server nggak bakal ngirim field lain yang mungkin ada di database, kayak password_hash, created_at, atau address — kecuali kamu minta.
Nested Query — Ini yang Bikin GraphQL Powerful
Nah, ini bagian yang bikin gue jatuh cinta sama GraphQL. Kamu bisa mengambil data relasional dalam satu request aja.
query {
user(id: "123") {
name
email
posts {
title
publishedAt
comments {
body
author {
name
}
}
}
}
}
Dalam satu query di atas, kamu udah ambil:
- Data user
- Semua post milik user itu
- Komentar di setiap post
- Nama author setiap komentar
Coba bandingin kalau pakai REST. Kamu minimal butuh 3-4 request berbeda: ambil user, ambil posts-nya, ambil comments tiap post, ambil data author tiap comment. Nggak efisien banget, terutama di mobile yang bandwidth-nya terbatas.
Query dengan Variable
Dalam praktik nyata, kamu nggak mau nge-hardcode ID di query. GraphQL mendukung variable supaya query bisa reusable:
query GetUser($userId: ID!) {
user(id: $userId) {
name
email
posts {
title
}
}
}
Variable-nya dikirim terpisah:
{
"userId": "123"
}
Tanda ! di $userId: ID! artinya wajib (non-nullable). Kalau nggak dikirim, server bakal nolak query-nya sebelum dieksekusi. Ini bagian dari sistem type GraphQL yang bikin aplikasi lebih robust.
Fragment — Biar Nggak Repeat Diri
Pernah nulis field yang sama di beberapa query? GraphQL punya solusinya: Fragment.
fragment UserBasicInfo on User {
name
email
avatar
}
query {
user(id: "123") {
...UserBasicInfo
posts {
title
}
}
currentUser {
...UserBasicInfo
role
}
}
Fragment bikin kode lebih DRY (Don’t Repeat Yourself) dan gampang di-maintain. Kalau ada perubahan field, cukup edit di satu tempat aja.
Mutation: Mengubah Data di Server
Kalau Query untuk membaca, maka Mutation untuk menulis — membuat, mengupdate, atau menghapus data. Di REST, ini setara dengan method POST, PUT, PATCH, dan DELETE.
Kenapa dipisah? Karena GraphQL punya jaminan: Query dijalankan secara paralel, Mutation dijalankan secara berurutan. Ini penting banget buat konsistensi data. Bayangin kamu bikin dua mutation sekaligus — bikin post lalu update profil. Kamu pasti mau yang pertama selesai dulu sebelum yang kedua jalan.
Contoh Mutation: Membuat Data Baru
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
body
publishedAt
author {
name
}
}
}
Variable-nya:
{
"input": {
"title": "Belajar GraphQL Itu Seru",
"body": "Tulisan pertama gue tentang GraphQL...",
"status": "PUBLISHED"
}
}
Response:
{
"data": {
"createPost": {
"id": "post_456",
"title": "Belajar GraphQL Itu Seru",
"body": "Tulisan pertama gue tentang GraphQL...",
"publishedAt": "2026-09-02T10:30:00Z",
"author": {
"name": "Budi Santoso"
}
}
}
Perhatikan polanya: mutation mengembalikan data juga. Ini salah satu keunggulan GraphQL — kamu bisa langsung tahu data apa yang tersimpan tanpa perlu request tambahan. Di REST, setelah POST, kadang kamu perlu GET lagi buat ambil data terbaru.
Contoh Mutation: Update Data
mutation UpdateProfile($userId: ID!, $input: UpdateProfileInput!) {
updateUser(id: $userId, input: $input) {
id
name
email
updatedAt
}
}
Variable:
{
"userId": "123",
"input": {
"name": "Budi Santoso Updated",
"email": "[email protected]"
}
}
Error Handling di Mutation
GraphQL punya pendekatan unik soal error. Bahkan kalau ada error, response HTTP-nya tetap 200 OK. Error ditempatkan di field errors terpisah:
{
"data": {
"createPost": null
},
"errors": [
{
"message": "Title must be at least 10 characters",
"path": ["createPost"],
"extensions": {
"code": "VALIDATION_ERROR",
"field": "title"
}
}
]
}
Ini awalnya agak aneh buat gue, tapi lama-lama gue paham filosofinya. GraphQL menganggap bahwa sebuah request bisa parsially berhasil — sebagian data berhasil diambil, sebagian gagal. Makanya HTTP status code nggak cukup buat menggambarkan situasi ini.
Saran gue: bikin helper function di frontend buat handle error GraphQL secara konsisten. Jangan cuma rely pada HTTP status code.
Subscription: Data Real-Time dari Server
Nah, ini konsep yang paling sering bikin orang penasaran sekaligus bingung. Subscription adalah operasi GraphQL untuk data real-time. Ketika ada perubahan data di server, client otomatis dapat notifikasi.
Di REST, kalau mau real-time, kamu biasanya pakai WebSocket, Server-Sent Events (SSE), atau polling (yang boros banget). Di GraphQL, real-time udah jadi bagian dari spesifikasi.
Cara Kerja Subscription
- Client mengirim subscription request ke server.
- Server membuka koneksi WebSocket.
- Ketika event yang di-subscribe terjadi, server mengirim data ke client.
- Koneksi tetap terbuka sampai client unsubscribe.
Contoh Subscription: Chat Real-Time
subscription OnNewMessage($chatroomId: ID!) {
messageAdded(chatroomId: $chatroomId) {
id
body
createdAt
author {
id
name
avatar
}
}
}
Variable:
{
"chatroomId": "room_789"
}
Setiap kali ada pesan baru di chatroom itu, server bakal push data ke client lewat WebSocket. Client tinggal render aja.
Contoh Subscription: Notifikasi
subscription OnNewNotification($userId: ID!) {
notificationReceived(userId: $userId) {
id
type
message
read
createdAt
relatedPost {
title
}
}
}
Implementasi Sederhana di Frontend
Kalau kamu pakai Apollo Client (library GraphQL paling populer di React), pakai subscription kurang lebih kayak gini:
import { useSubscription, gql } from '@apollo/client';
const NEW_MESSAGE_SUBSCRIPTION = gql`
subscription OnNewMessage($chatroomId: ID!) {
messageAdded(chatroomId: $chatroomId) {
id
body
author {
name
}
}
}
`;
function ChatRoom({ chatroomId }) {
const { data, loading, error } = useSubscription(
NEW_MESSAGE_SUBSCRIPTION,
{ variables: { chatroomId } }
);
if (loading) return <p>Menunggu pesan...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div className="message">
<strong>{data.messageAdded.author.name}</strong>
<p>{data.messageAdded.body}</p>
</div>
);
}
Kapan Harus Pakai Subscription?
Subscription memang keren, tapi jangan pakai sembarangan. Beberapa pertimbangan:
- Pakai kalau: chat, notifikasi, live dashboard, collaborative editing, tracking lokasi real-time.
- Hindari kalau: data berubah jarang, atau polling tiap 30 detik udah cukup.
- Pertimbangkan skala: setiap subscription = 1 koneksi WebSocket yang terus-menerus terbuka. Di skala besar, ini butuh resource server yang signifikan.
Schema: Fondasi dari Semuanya
Sebelum kita tutup, gue mau bahas satu konpek lagi yang fundamental: Schema. Tanpa schema, nggak ada GraphQL.
Schema adalah blueprint yang mendefinisikan semua type, query, mutation, dan subscription yang bisa dilakukan. Ditulis dalam bahasa yang disebut Schema Definition Language (SDL).
type User {
id: ID!
name: String!
email: String!
avatar: String
posts: [Post!]!
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
body: String!
status: PostStatus!
author: User!
comments: [Comment!]!
publishedAt: DateTime
}
type Comment {
id: ID!
body: String!
author: User!
post: Post!
createdAt: DateTime!
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
posts(status: PostStatus): [Post!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post!
deletePost(id: ID!): Boolean!
addComment(postId: ID!, body: String!): Comment!
}
type Subscription {
messageAdded(chatroomId: ID!): Message!
notificationReceived(userId: ID!): Notification!
}
Schema ini jadi “kontrak” antara frontend dan backend. Frontend developer bisa langsung lihat schema dan tahu data apa saja yang tersedia, tipe datanya apa, dan operasi apa yang bisa dilakukan — tanpa perlu baca dokumentasi terpisah atau nanya ke backend developer.
Ini salah satu keunggulan besar GraphQL untuk kolaborasi tim.
Tools yang Wajib Diketahui Pemula
Sebagai penutup, gue mau rekomendasiin beberapa tools yang bakal bikin hidup kamu lebih gampang saat belajar GraphQL:
GraphiQL / GraphQL Playground — IDE interaktif buat nulis dan ngetes query. Auto-complete, dokumentasi built-in, dan syntax highlighting. Biasanya udah include di development server.
Apollo Client — Library paling populer buat pakai GraphQL di frontend. Support React, Vue, Angular, dan vanilla JS. Punya caching yang canggih.
Apollo Server — Buat bikin GraphQL server di Node.js. Setup-nya cepat dan fleksibel.
Hasura — Instant GraphQL API dari database PostgreSQL. Cocok buat prototyping cepat.
GraphQL Code Generator — Auto-generate TypeScript types dari schema GraphQL. Nggak perlu nulis type manual.
Mulai dari Mana?
Kalau kamu baru mau mulai belajar, saran gue:
- Main-main dulu di GraphQL Official Tutorial — dokumentasinya jelas dan interaktif.
- Coba bikin API sederhana pakai Apollo Server di Node.js.
- Buat frontend yang consume API itu pakai Apollo Client.
- Setelah paham flow-nya, explore tools kayak Hasura buat project yang lebih serius.
Nggak perlu langsung paham semuanya. Mulai dari Query, bikin beberapa mutation, baru lama-lama masuk ke subscription. Seperti belajar hal baru lainnya — konsistensi dan praktik itu kunci.
Ada pertanyaan soal GraphQL yang belum terjawab di artikel ini? Atau butuh bantuan implementasi GraphQL di project kamu? Jangan ragu buat reach out ke gue di [email protected] — gue seneng banget bisa diskusi dan bantu sebisa mungkin!
FAQ (Pertanyaan yang Sering Ditanyakan)
1. GraphQL itu lebih bagus dari REST?
Nggak juga. Tergantung kebutuhan. REST lebih simpel buat API yang straightforward dan udah punya banyak tooling mature. GraphQL unggul kalau frontend kamu butuh data yang fleksibel, ada banyak relasi antar data, atau kamu mau mengurangi jumlah HTTP request. Banyak perusahaan besar pakai keduanya berdampingan — REST buat endpoint simpel, GraphQL buat kebutuhan yang lebih kompleks.
2. Apakah GraphQL aman?
GraphQL sendiri bukan lebih atau kurang aman dari REST. Yang perlu kamu perhatiin adalah: query complexity attack — di mana attacker bisa bikin query super nested yang bikin server nge-loop terus-terusan. Solusinya: implementasikan query depth limiting, rate limiting, dan query cost analysis. Selain itu, tetap terapkan autentikasi dan otorisasi seperti biasa di resolver.
3. GraphQL cocok untuk project kecil atau hanya untuk enterprise?
Cocok untuk semua skala, tapi ada trade-off. Untuk project kecil yang CRUD-nya sederhana, REST mungkin lebih cepat dibangun. Tapi kalau project kamu punya banyak entity yang saling berelasi, atau kamu bikin app yang datanya dikonsumsi oleh berbagai platform (web, mobile, IoT), GraphQL bakal ngasih ROI yang lebih besar seiring waktu. Tools kayak Hasura juga bikin entry point ke GraphQL jadi lebih mudah buat project skala kecil-menengah.
4. Apakah Subscription menggantikan WebSocket?
Bukan menggantikan, tapi memanfaatkan. Subscription di GraphQL biasanya diimplementasikan di atas WebSocket (atau kadang SSE). GraphQL memberikan abstraction layer di atas WebSocket — kamu nggak perlu manage koneksi WebSocket secara manual. Cukup tulis subscription, library (seperti Apollo) yang handle sisanya.
5. Bagaimana cara handle caching di GraphQL?
Ini salah satu tantangan unik GraphQL. Karena semua data lewat satu endpoint, HTTP caching (yang biasa dipakai di REST) nggak bisa langsung diterapkan. Solusinya: pakai client-side caching dari Apollo Client (normalized cache) atau urql. Di sisi server, kamu bisa implementasikan DataLoader untuk batching dan caching query ke database.