REST mendukung 83% dari semua layanan web dan 76% API publik. GraphQL telah melihat lonjakan 340% dalam adopsi Fortune 500 sejak 2023. Dua fakta ini hidup berdampingan tanpa kontradiksi: REST tetap menjadi default, GraphQL mendapatkan momentum serius dalam konteks enterprise. Saya telah membangun API produksi di keduanya, dan pandangan saya pragmatis — GraphQL benar-benar lebih baik untuk masalah tertentu, dan REST benar-benar lebih baik untuk masalah lain.
REST menang dalam kesederhanaan, caching, dan kematangan ekosistem. API REST adalah sekumpulan URL dengan HTTP verb — klien HTTP mana pun dalam bahasa apa pun dapat mengonsumsinya tanpa library. Respons GET dapat di-cache secara default di lapisan HTTP (CDN, cache browser, cache proxy) dengan header Cache-Control standar. Ekosistem tooling sangat besar: Swagger/OpenAPI untuk dokumentasi, Postman untuk pengujian.
REST mencapai ~250ms waktu respons median untuk permintaan sumber daya standar. Bottleneck performa untuk REST biasanya overfetching (mendapatkan lebih banyak bidang dari yang diperlukan) dan beberapa round trip (permintaan N+1 untuk mendapatkan sumber daya terkait). Untuk klien mobile yang membutuhkan profil pengguna, pesanan terbaru, dan jumlah notifikasi dalam satu layar, REST memerlukan tiga panggilan API terpisah.
GraphQL menang dalam presisi pengambilan data dan hubungan data yang kompleks. Satu kueri GraphQL dapat mengambil pengguna, 5 pesanan terakhir mereka, item setiap pesanan, dan detail produk — dalam satu round trip, dengan tepat bidang yang dibutuhkan klien. GraphQL mengurangi panggilan API hingga 60% dalam skenario agregasi data yang kompleks dan mencapai ~180ms latensi median untuk kueri yang akan memerlukan beberapa round trip REST.
REST: 3 round trips to build a user dashboard
─────────────────────────────────────────────
Client → GET /api/users/42 → { id, name, email }
Client → GET /api/users/42/orders → [{ id, total, status }]
Client → GET /api/notifications/42 → { unread: 3 }
Total: 3 requests, ~750ms on mobile 4G
GraphQL: 1 round trip, exact fields
─────────────────────────────────────────────
Client → POST /graphql
query {
user(id: "42") {
name
email
recentOrders(limit: 5) { id total status }
notificationCount
}
}
→ { user: { name, email, recentOrders: [...], notificationCount: 3 } }
Total: 1 request, ~180ms on mobile 4G ✓
Adoption (2025):
REST: 83% of all web services, 76% of public APIs
GraphQL: 340% growth in Fortune 500 adoption since 2023
67% of adopters report improved dev productivity
89% would choose GraphQL again for similar projectsDari pengalaman saya membangun API NestJS: gunakan GraphQL untuk API yang menghadap klien di mana beberapa klien (web, mobile, mitra) mengambil data yang sama dalam bentuk berbeda. Gunakan REST untuk integrasi server-ke-server, API publik, dan operasi CRUD sederhana. Pendekatan hybrid — GraphQL untuk API frontend, REST untuk panggilan microservice-ke-microservice — bekerja dengan baik karena mengoptimalkan setiap lapisan untuk kasus penggunaan aktualnya.
Fleksibilitas GraphQL menciptakan tantangan produksi yang tidak ada di REST. Masalah N+1: kueri GraphQL dapat memicu N kueri database untuk N item dalam daftar. Solusinya adalah DataLoader, yang membatch dan mendeduplikasi panggilan database. Kompleksitas kueri: klien dapat menulis kueri bersarang dalam yang menyebabkan beban database eksponensial. Caching: respons GraphQL tidak dapat di-cache di lapisan HTTP secara default karena semua permintaan pergi ke POST /graphql.
API REST memiliki area permukaan yang terdefinisi — setiap endpoint eksplisit. GraphQL memiliki bahasa kueri terbuka. Ini membuat otorisasi lebih kompleks: setiap resolver bidang harus memeriksa apakah pengguna yang meminta diizinkan untuk melihat bidang itu. Di REST, Anda memeriksa otorisasi per endpoint. Di GraphQL, Anda memeriksa per bidang — melewatkan pemeriksaan bidang adalah potensi kebocoran data.
// app.module.ts — NestJS with both REST and GraphQL
import { GraphQLModule } from '@nestjs/graphql'
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true, // generate schema from decorators
sortSchema: true,
introspection: process.env.NODE_ENV !== 'production', // security!
playground: process.env.NODE_ENV !== 'production',
}),
],
})
// invoices.resolver.ts — GraphQL resolver
@Resolver(() => Invoice)
export class InvoicesResolver {
constructor(
private invoicesService: InvoicesService,
private readonly invoiceItemsLoader: InvoiceItemsLoader, // DataLoader for N+1
) {}
@Query(() => [Invoice])
async invoices(@Args('cursor') cursor?: string): Promise<Invoice[]> {
return this.invoicesService.findAll(cursor)
}
@ResolveField(() => [InvoiceItem])
async items(@Parent() invoice: Invoice): Promise<InvoiceItem[]> {
// DataLoader batches all invoice item requests into one DB query
return this.invoiceItemsLoader.load(invoice.id)
}
@Subscription(() => Invoice)
invoiceUpdated() {
return pubSub.asyncIterator('invoiceUpdated') // real-time via WebSocket
}
}
// invoices.controller.ts — REST controller (same service layer)
@Controller({ path: 'invoices', version: '2' })
export class InvoicesController {
constructor(private readonly invoicesService: InvoicesService) {}
@Get()
findAll(@Query('cursor') cursor?: string) {
return this.invoicesService.findAll(cursor) // same service method!
}
}GraphQL subscriptions melalui WebSockets menyediakan push data real-time — klien berlangganan event dan menerima pembaruan tanpa polling. GraphQL subscriptions mengurangi overhead polling hingga 80% dibandingkan polling REST untuk data real-time. Untuk sistem ERP di mana pengguna perlu melihat pembaruan status pesanan secara real-time, subscription GraphQL lebih bersih.
Fitur introspeksi GraphQL memungkinkan klien mengkueri skema penuh — setiap tipe, bidang, dan hubungan. Dalam pengembangan, ini mendukung GraphQL Playground dan pembuatan tipe. Dalam produksi, ini mengekspos seluruh model data Anda kepada siapa pun yang dapat menjangkau endpoint Anda. Nonaktifkan introspeksi di produksi: `introspection: process.env.NODE_ENV !== 'production'`.
Saya menggunakan pohon keputusan ini: Beberapa klien dengan kebutuhan data berbeda → GraphQL. CRUD sederhana dengan bentuk data standar → REST. API publik dengan pengembang pihak ketiga → REST. Data real-time → subscriptions GraphQL. Caching kritis → REST atau GraphQL dengan persisted queries. Tim baru di codebase → REST (kurva belajar lebih rendah). Hubungan entitas kompleks, agregasi dashboard → GraphQL.
NestJS membuatnya mudah untuk menjalankan REST dan GraphQL secara bersamaan dalam aplikasi yang sama. REST controllers dan GraphQL resolver dapat berbagi lapisan layanan yang sama. Arsitektur ini memungkinkan Anda mulai dengan REST dan menambahkan GraphQL nanti tanpa menulis ulang logika bisnis Anda.