Pernah nggak kamu frustrasi sama REST API yang over-fetching atau under-fetching? Kemarin saya lagi bikin dashboard buat nampilin data user. Di REST, saya butuh nama user, list post-nya, dan komen di tiap post. Artinya hit 3 endpoint berbeda, tunggu response berganda, dan payload yang balik kadang isinya 50 field padahal yang saya butuh cuma 5. Nah, situasi kayak gini yang bikin saya penasaran sama GraphQL.
GraphQL itu query language buat API yang Facebook (sekarang Meta) bikin tahun 2012 terus open-source tahun 2015. Intinya, GraphQL ngasih kamu kekuatan buat minta persis data yang kamu butuhkan. Nggak lebih, nggak kurang. Satu request, satu response, selesai.
Kenapa GraphQL Bukan REST?
Buat yang baru kenal, perbedaan utamanya gampang banget dimengerti. Di REST, kamu bikin banyak endpoint: /users, /users/1/posts, /users/1/posts/5/comments. Tiap endpoint balikin struktur data yang udah ditentukan sama backend. Kamu nggak bisa ngatur mau ambil field mana aja.
Di GraphQL, kamu punya satu endpoint aja - biasanya /graphql. Dari situ, kamu kirim query yang nentuin field mana yang mau diambil. Backend balikin JSON persis sesuai query kamu. Sederhana, tapi dampaknya lumayan besar buat produktivitas.
# REST: butuh 3 request buat dapetin data ini
# GET /users/1
# GET /users/1/posts
# GET /posts/5/comments
# GraphQL: cukup 1 request!
query {
user(id: 1) {
name
email
posts(limit: 5) {
title
createdAt
comments(limit: 3) {
author
text
}
}
}
}
Response yang balik bakal mirror persis sama query kamu. Field yang kamu minta, itulah yang balik. Nggak ada field yang kepotong, nggak ada field yang nggak dipakai. Buat mobile developer yang concern sama data usage, ini emas.
Setup Server GraphQL dengan Node.js
Oke, sekarang kita praktik. Saya pakai Node.js dengan Apollo Server karena dokumentasinya lengkap dan community-nya aktif banget. Kalau kamu lebih suka Python, bisa pakai Graphene atau Strawberry. Tapi konsepnya sama aja.
Pertama, install dependencies-nya:
mkdir graphql-demo && cd graphql-demo
npm init -y
npm install @apollo/server graphql
Buat file index.js dengan schema sederhana. Schema di GraphQL itu kayak kontrak - kamu nentukan tipe data apa yang ada, field apa yang tersedia, dan gimana cara ngambilnya.
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
// Definisi tipe data (Type Definitions)
const typeDefs = `#graphql
type Post {
id: ID!
title: String!
content: String
createdAt: String!
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(userId: ID!, title: String!, content: String): Post!
}
`;
Bagian type Query itu buat baca data (kayak GET di REST), sedangkan type Mutation buat ubah data (POST, PUT, DELETE). Nggak ada aturan baku, tapi konvensi ini udah dipakai mayoritas developer GraphQL.
Resolvers: Jembatan antara Schema dan Data
Schema cuma nentuin "bentuk" data. Yang bener-bener ngambil data itu namanya resolver. Tiap field di schema punya resolver-nya sendiri. Ini yang powerful - kamu bisa sambungkan ke database, API lain, bahkan service micro lain tanpa client peduli datanya dari mana.
// Data dummy buat demo
const users = [
{ id: '1', name: 'Budi Santoso', email: '[email protected]' },
{ id: '2', name: 'Sari Dewi', email: '[email protected]' }
];
const posts = [
{ id: '1', userId: '1', title: 'Belajar GraphQL', content: 'Awalnya ribet...', createdAt: '2026-08-14' },
{ id: '2', userId: '1', title: 'Node.js Tips', content: 'Pakai cluster...', createdAt: '2026-08-13' },
{ id: '3', userId: '2', title: 'CSS Container Queries', content: 'Game changer...', createdAt: '2026-08-12' }
];
// Resolvers
const resolvers = {
Query: {
users: () => users,
user: (_, { id }) => users.find(u => u.id === id),
posts: () => posts
},
Mutation: {
createUser: (_, { name, email }) => {
const user = { id: String(users.length + 1), name, email };
users.push(user);
return user;
},
createPost: (_, { userId, title, content }) => {
const post = {
id: String(posts.length + 1),
userId,
title,
content,
createdAt: new Date().toISOString().split('T')[0]
};
posts.push(post);
return post;
}
},
// Field-level resolver: ambil posts berdasarkan userId
User: {
posts: (parent) => posts.filter(p => p.userId === parent.id)
}
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });
console.log(`Server siap di ${url}`);
Jalanin dengan node index.js lalu buka http://localhost:4000. Kamu bakal nemuin Apollo Sandbox - interactive playground buat test query langsung dari browser. Ini salah satu kelebihan GraphQL: tooling-nya bener-bener matang dibanding REST yang sering cuma andal Postman.
Query dan Mutation dalam Praktik
Sekarang coba test di Apollo Sandbox. Bisa pakai query buat ambil data:
# Ambil semua user dengan posts mereka
query GetUsersWithPosts {
users {
id
name
posts {
title
createdAt
}
}
}
Atau buat user baru dengan mutation:
mutation CreateNewUser {
createUser(name: "Andi Pratama", email: "[email protected]") {
id
name
email
}
}
Yang keren itu, client bisa minta field yang dibutuhkan aja. Mobile app butuh name dan email? Minta dua field itu. Web dashboard butuh posts sama comments? Tambah di query. Semua dari satu endpoint yang sama, tanpa ubah backend sedikitpun.
Integrasi dengan Database PostgreSQL
Demo di atas pakai data dummy. Di production, kamu bakal sambung ke database. Untuk PostgreSQL, ada Hasura - engine yang otomatis generate GraphQL API dari schema database kamu. Setup-nya gampang banget:
# Jalankan Hasura dengan Docker
docker run -d -p 8080:8080 \
-e HASURA_GRAPHQL_DATABASE_URL=postgres://user:pass@localhost:5432/mydb \
-e HASURA_GRAPHQL_ENABLE_CONSOLE=true \
hasura/graphql-engine:latest
Buka http://localhost:8080 dan kamu langsung punya GraphQL API lengkap dengan filtering, sorting, pagination, bahkan subscription (real-time) tanpa nulis satu baris pun resolver. Buat tim kecil yang mau cepat launch, ini sangat produktif.
Alternatif lain, Prisma ORM + Apollo Server. Prisma generate type-safe client dari schema database kamu, jadi resolver jadi lebih aman dari sisi TypeScript:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const resolvers = {
Query: {
users: () => prisma.user.findMany({
include: { posts: true }
}),
user: (_, { id }) => prisma.user.findUnique({
where: { id: Number(id) },
include: { posts: true }
})
},
Mutation: {
createUser: (_, { name, email }) =>
prisma.user.create({ data: { name, email } })
}
};
Performance: N+1 Problem dan DataLoader
GraphQL punya satu masalah klasik yang sering bikin performance turun: N+1 query problem. Bayangkan kamu ambil 10 user, terus tiap user punya resolver posts yang query database. Itu berarti 1 query buat ambil user + 10 query buat ambil posts tiap user = 11 query total. Kalau posts punya resolver comments juga, multiply lagi.
Solusinya pakai DataLoader dari Facebook. DataLoader nge-batch query database jadi satu request besar alih-alih N request kecil:
import DataLoader from 'dataloader';
// Batch function: ambil semua posts sekaligus
const batchPosts = async (userIds) => {
const allPosts = await prisma.post.findMany({
where: { userId: { in: userIds.map(Number) } }
});
// Group by userId
return userIds.map(id =>
allPosts.filter(p => p.userId === Number(id))
);
};
const postLoader = new DataLoader(batchPosts);
const resolvers = {
User: {
posts: (parent) => postLoader.load(parent.id)
}
};
Dengan DataLoader, 11 query jadi 2 query saja. Performance boost-nya signifikan, terutama kalau data kamu nested dalam beberapa level.
GraphQL vs REST: Kapan Pakai Mana?
Jujur, GraphQL bukan silver bullet. Kadang REST masih lebih cocok. Ini perbandingan praktis berdasarkan pengalaman saya:
- Mobile app: GraphQL lebih unggul. Hemat data, satu request dapat semua yang dibutuhkan, dan client bisa optimalkan payload per screen.
- Microservices: GraphQL bagus buat API gateway. Aggregate data dari multiple service dalam satu query. Tapi kalau service-nya cuma satu dan simple, REST cukup.
- File upload/download: REST masih menang. Upload file besar lebih natural di REST. GraphQL multipart upload bisa, tapi setup-nya lebih ribet.
- Caching: REST menang di HTTP caching (CDN, browser cache). GraphQL pakai caching di level client (Apollo Cache) yang powerful tapi butuh konfigurasi.
- Real-time: GraphQL Subscription (via WebSocket) lebih konsisten dengan query/mutation pattern. REST bisa pakai WebSocket/SSE tapi polanya beda.
- Tim kecil, launch cepat: Hasura atau Supabase bikin GraphQL API dari database dalam menit. Buat MVP, ini susah dilawan.
Deployment ke Production
Untuk deploy GraphQL server, cara termudah pakai Docker. Bikin Dockerfile sederhana:
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]
Build dan jalankan di VPS kamu. Kalau pakai nginx sebagai reverse proxy, arahkan /graphql ke container GraphQL-nya. SSL pakai Let's Encrypt dengan Certbot, gratis dan setup-nya 5 menit.
Beberapa hosting platform yang support GraphQL natively:
- Railway - deploy dari GitHub repo, auto-scale, ada PostgreSQL built-in. Cocok buat project GraphQL + Prisma.
- Render - alternatif Railway, free tier tersedia buat experiment.
- Fly.io - deploy ke edge locations, cocok kalau target audience global.
- VPS (DigitalOcean/Hetzner) - full control, paling murah buat traffic tinggi. Tapi kamu handle infra sendiri.
Security: Jangan Lupa Rate Limiting
GraphQL punya satu issue security yang unique: query depth attack. Attacker bisa kirim query nested sangat dalam yang bikin server kalkulasi berlebihan. Contoh:
# Attack: query nested 1000 level
query {
user { posts { author { posts { author { posts { ... } } } } } }
}
Kalau nggak ada protection, ini bisa bikin server hang atau OOM. Solusinya pakai graphql-depth-limit dan graphql-cost-analysis:
import depthLimit from 'graphql-depth-limit';
import costAnalysis from 'graphql-cost-analysis';
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
depthLimit(10), // max 10 level nested
costAnalysis({
maximumCost: 1000,
variables: { count: 10 }
})
]
});
Rate limiting juga penting. Pakai apollo-server-rate-limiter atau handle di nginx level dengan limit_req. Karena GraphQL cuma satu endpoint, rate limiting per-IP cukup efektif.
Tooling yang Bikin GraphQL Enak Dipakai
Salah satu alasan GraphQL cepat populer adalah tooling-nya. Beberapa tools yang wajib kamu coba:
- Apollo Client DevTools - browser extension buat inspect query, cache, dan mutation. Bikin debugging jadi jauh lebih gampang.
- GraphQL Code Generator - generate TypeScript types dari schema. Kalau kamu pakai TypeScript, ini wajib. Type safety dari schema sampai component.
- GraphiQL / Apollo Sandbox - interactive query editor dengan autocomplete. Field apa saja yang tersedia langsung kelihatan.
- GraphQL Inspector - CI tool buat detect breaking changes di schema. Bikin schema evolution jadi lebih aman di tim besar.
Kesimpulan
GraphQL ngerubah cara kita mikir soal API. Daripada bikin banyak endpoint dengan struktur kaku, kita kasih client kekuatan buat minta data sesuai kebutuhan. Buat aplikasi yang data-nya nested dan kompleks (dashboard, social media, e-commerce), GraphQL jelas worth dipertimbangkan.
Tapi ingat, REST masih relevan. Kalau API kamu simple, CRUD standard, dan nggak butuh flexibility query, REST tetap pilihan yang solid dan familiar. Yang penting, pilih tool sesuai masalah yang mau diselesaikan, bukan karena hype.
Kalau kamu udah pernah pakai GraphQL di project, pengalaman kayak gimana? Rest atau GraphQL yang lebih cocok buat kasus kamu? Bagi dong di kolom komentar, saya penasaran denger perspektif kamu.