Fundamental #
Hampir semua sistem software modern berkomunikasi melalui API — mobile app memanggil backend, microservice memanggil service lain, frontend meminta data dari server, sistem internal mengintegrasikan tool yang berbeda. Tapi banyak engineer yang menggunakan API setiap hari tanpa benar-benar memahami mengapa ia dirancang seperti itu, mengapa ada begitu banyak jenisnya, dan bagaimana memilih yang tepat untuk konteks yang berbeda. Artikel ini membangun pemahaman dari dasar: apa itu API, bagaimana ia berevolusi, apa prinsip-prinsip yang membuatnya baik, dan kapan menggunakan REST, GraphQL, gRPC, JSON-RPC, atau tRPC.
Apa Itu API? #
API, singkatan dari Application Programming Interface, adalah kontrak komunikasi antar sistem. Ia mendefinisikan apa yang bisa diakses, bagaimana cara mengaksesnya, format data yang dikirim dan diterima, serta batasan dalam interaksi tersebut.
Kata kunci yang paling penting adalah kontrak dan interface — bukan implementasi. Implementasi boleh berubah di balik layar; kontraknya tidak (atau sejarang mungkin). Consumer API tidak perlu tahu bagaimana data disimpan, bagaimana logika bisnis dijalankan, atau library apa yang digunakan — mereka hanya perlu tahu kontraknya.
Ilustrasi posisi API dalam arsitektur sistem:
flowchart TD
Client["Client / Consumer\n(Browser, Mobile, Service Lain)"]
API["API Layer\n(Contract & Boundary)"]
BL["Business Logic\n(Service / Domain)"]
DS["Data Source\n(DB / Cache / Message Queue)"]
Client -->|"Request\n(HTTP / RPC / Query)"| API
API -->|"Call / Command"| BL
BL -->|"Read / Write"| DS
DS -->|"Data"| BL
BL -->|"Result"| API
API -->|"Response\n(Data / Error)"| Client
style API fill:#4A90D9,color:#fff,stroke:#2C6FACDari diagram ini ada satu hal penting: client tidak berinteraksi langsung dengan business logic atau database. API bertindak sebagai boundary — ia melindungi internal sistem dari dunia luar, dan melindungi consumer dari perubahan internal yang tidak perlu mereka ketahui.
Sejarah Evolusi API #
Memahami sejarah API membantu memahami mengapa berbagai jenis API yang ada sekarang masing-masing dirancang untuk memecahkan masalah yang berbeda — bukan karena yang baru selalu lebih baik, melainkan karena konteks yang berbeda membutuhkan solusi yang berbeda.
timeline
title Evolusi API
1960 : Library & Function Call
: Komunikasi dalam satu proses
: Standard library C
1980 : RPC & Distributed System
: Komunikasi antar mesin
: CORBA, DCOM
: Tightly coupled, kompleks
2000 : HTTP API & REST
: Stateless, web-scale
: Interoperabilitas tinggi
: Roy Fielding, 2000
2012 : GraphQL
: Facebook internal, 2012
: Open-source 2015
: Mengatasi over-fetching REST
2015 : gRPC
: Google, HTTP/2 + Protobuf
: Performa tinggi
: Service-to-service
2020 : tRPC
: Ekosistem TypeScript
: End-to-end type safety
: Fullstack monorepoEra Awal: Library dan Function Call (1960–1980) #
API pertama bukan yang berjalan di atas jaringan — ia adalah function call dalam satu sistem. Standard library di C adalah API: developer memanggil printf() tanpa perlu tahu bagaimana output dirender ke terminal. Konsepnya sama persis dengan API modern: sembunyikan implementasi, ekspos interface.
Masalahnya: API ini hanya bekerja dalam satu environment, satu proses, satu mesin.
Era Distributed System dan RPC (1980–1990) #
Kebutuhan komunikasi antar mesin melahirkan RPC (Remote Procedure Call) — memungkinkan kode di mesin A memanggil fungsi yang berjalan di mesin B seolah-olah lokal. CORBA dan DCOM adalah implementasi populer di era ini.
Masalahnya: tightly coupled (consumer dan provider harus menggunakan framework yang sama), kompleks, sulit di-debug, dan tidak bekerja baik di jaringan yang tidak reliable.
Era Web dan HTTP API (2000–sekarang) #
HTTP yang awalnya dirancang untuk dokumen web ternyata menjadi protokol yang sempurna untuk API: stateless, text-based, mudah di-debug, dan bekerja di setiap jaringan. Roy Fielding memformalisasi prinsip-prinsip ini sebagai REST di disertasinya tahun 2000.
Dampaknya transformatif: siapapun bisa membangun API yang bisa dikonsumsi oleh siapapun, menggunakan bahasa pemrograman apapun, dari manapun di dunia.
Era Modern: Mobile, Microservices, dan Realtime (2010–sekarang) #
Skala yang lebih besar membawa masalah baru. Mobile app dengan bandwidth terbatas tidak suka over-fetching data. Ratusan microservice yang saling berkomunikasi membutuhkan protokol yang lebih efisien dari HTTP/JSON. Tim fullstack TypeScript ingin type safety end-to-end tanpa duplikasi schema.
Hasilnya: GraphQL, gRPC, dan tRPC — masing-masing muncul untuk menjawab masalah spesifik yang tidak diselesaikan dengan baik oleh REST.
Lima Prinsip Fundamental API yang Baik #
1. Contract First #
API adalah perjanjian. Sebelum menulis satu baris implementasi, kontraknya harus didefinisikan terlebih dahulu: endpoint apa yang ada, parameter apa yang diterima, response apa yang dikembalikan, dan error apa yang mungkin terjadi.
// ANTI-PATTERN: Implementation first
Menulis kode dulu, lalu dokumentasi API dibuat belakangan
→ API mencerminkan detail implementasi, bukan kebutuhan consumer
→ Kontrak berubah setiap kali implementasi berubah
→ Consumer tidak punya pegangan yang stabil
// BENAR: Contract first
Definisikan kontrak API terlebih dahulu (OpenAPI spec, Protobuf schema, GraphQL SDL)
→ Consumer dan provider bisa bekerja paralel berdasarkan kontrak
→ Implementasi bebas berubah selama kontrak terpenuhi
→ Breaking change terdeteksi lebih awal
2. Abstraction #
API menyembunyikan detail internal — struktur database, library yang digunakan, cara penyimpanan data, arsitektur internal. Consumer hanya perlu tahu apa yang bisa dilakukan, bukan bagaimana ia dilakukan.
// ANTI-PATTERN: Membocorkan detail internal
GET /api/mysql_users_table?id=123
Response: { "mysql_id": 123, "created_timestamp_unix": 1706352000 }
→ Consumer tahu kita pakai MySQL
→ Jika kita pindah ke PostgreSQL, field name harus berubah → breaking change
// BENAR: Abstraksi yang bersih
GET /api/users/123
Response: { "id": "usr_123", "created_at": "2024-01-27T10:00:00Z" }
→ Consumer tidak tahu (dan tidak perlu tahu) database yang digunakan
→ Internal bisa berubah tanpa breaking change
3. Consistency #
API yang baik terasa predictable — jika satu endpoint berperilaku dengan cara tertentu, endpoint yang lain harus mengikuti pola yang sama. Inkonsistensi memaksa consumer mempelajari setiap endpoint secara individual.
// ANTI-PATTERN: Inkonsistensi naming dan behavior
GET /api/getUser/123 → mendapatkan user
POST /api/users/create → membuat user baru
DELETE /api/remove-user/123 → menghapus user
Error response 1: { "error": "not found" }
Error response 2: { "message": "User does not exist", "code": 404 }
Error response 3: { "status": "error", "detail": "..." }
// BENAR: Konsistensi yang predictable
GET /api/users/123 → mendapatkan user
POST /api/users → membuat user baru
DELETE /api/users/123 → menghapus user
Semua error: { "error": { "code": "USER_NOT_FOUND", "message": "..." } }
4. Backward Compatibility #
API yang matang berusaha sekeras mungkin untuk tidak merusak consumer yang sudah ada. Setiap breaking change membutuhkan koordinasi dengan semua pihak yang menggunakan API — dan dalam konteks public API, itu bisa berarti ribuan consumer.
Strategi menjaga backward compatibility:
Penambahan field response → AMAN (consumer yang tidak paham field baru bisa abaikan)
Menghapus field response → BREAKING CHANGE
Mengubah tipe field → BREAKING CHANGE
Menambahkan required parameter → BREAKING CHANGE
Mengubah behavior endpoint → TERGANTUNG kontrak yang ada
Jika breaking change tidak bisa dihindari → versioning:
/api/v1/users → perilaku lama, tetap didukung
/api/v2/users → perilaku baru
5. Explicit Over Implicit #
API yang baik lebih memilih verbose tapi jelas daripada ringkas tapi ambigu. Nama endpoint, parameter, dan field harus self-explanatory — consumer tidak boleh harus membaca source code atau documentasi panjang untuk memahami apa yang dimaksud.
// ANTI-PATTERN: Implisit dan ambigu
POST /api/process
Body: { "type": 1, "data": "..." }
→ "process" apa? "type 1" berarti apa?
// BENAR: Eksplisit dan jelas
POST /api/orders/checkout
Body: { "payment_method": "credit_card", "items": [...] }
→ Jelas tujuannya, jelas isinya
Klasifikasi API #
Berdasarkan Aksesibilitas #
flowchart LR
subgraph Public["Public API"]
P["Terbuka untuk umum\nDokumentasi publik\nContoh: Twitter API, Stripe API"]
end
subgraph Partner["Partner API"]
PA["Terbatas untuk mitra bisnis\nPerjanjian akses khusus\nContoh: B2B integration"]
end
subgraph Private["Private / Internal API"]
PR["Hanya internal organisasi\nTidak di-expose ke luar\nContoh: service-to-service"]
end
Public --> |"Lebih ketat\nrate limiting"| Partner
Partner --> |"Lebih longgar\nno auth overhead"| Private
style Public fill:#27AE60,color:#fff,stroke:#1E8449
style Partner fill:#F39C12,color:#fff,stroke:#D68910
style Private fill:#2C3E50,color:#fff,stroke:#1A252FKlasifikasi ini penting karena memengaruhi keputusan desain: public API membutuhkan dokumentasi yang jauh lebih lengkap, versioning yang lebih ketat, rate limiting, dan backward compatibility yang lebih dijaga dibanding internal API.
Berdasarkan Gaya Komunikasi #
flowchart TD
subgraph Resource["Resource-based"]
R["Operasi pada resource\nGET /users/123\nREST"]
end
subgraph Query["Query-based"]
Q["Consumer tentukan data\nquery { user(id: 123) { name } }\nGraphQL"]
end
subgraph Procedure["Procedure-based"]
P["Panggil prosedur spesifik\ngetUser(id: 123)\ngRPC, JSON-RPC"]
end
subgraph Contract["Contract-based (Type-safe)"]
C["Type inference antar layer\ntanpa manual schema\ntRPC"]
endJenis-Jenis Implementasi API #
REST #
REST (Representational State Transfer) diperkenalkan Roy Fielding di disertasinya tahun 2000. Ia bukan protokol — ia adalah architectural style yang memanfaatkan HTTP semantics: URL merepresentasikan resource, HTTP method merepresentasikan operasi.
Karakteristik REST:
→ Stateless: setiap request membawa semua informasi yang dibutuhkan
→ Resource-oriented: URL adalah noun, bukan verb
→ HTTP verb sebagai operasi: GET (baca), POST (buat), PUT/PATCH (update), DELETE (hapus)
→ Response format fleksibel, paling umum JSON
Contoh yang benar:
GET /users → list semua user
GET /users/123 → ambil user dengan id 123
POST /users → buat user baru
PATCH /users/123 → update sebagian data user 123
DELETE /users/123 → hapus user 123
Contoh yang salah (bukan REST):
GET /getUsers → verb di URL
POST /users/create → verb di URL
POST /deleteUser/123 → operasi delete lewat POST
// BENAR: HTTP status code yang tepat
200 OK → request berhasil, ada data
201 Created → resource baru berhasil dibuat
204 No Content → berhasil, tidak ada data dikembalikan
400 Bad Request → input tidak valid
401 Unauthorized→ belum autentikasi
403 Forbidden → autentikasi ada, tapi tidak punya akses
404 Not Found → resource tidak ditemukan
422 Unprocessable → validasi bisnis gagal
500 Internal Error → error tak terduga di server
// ANTI-PATTERN: Selalu return 200
HTTP 200 OK
Body: { "success": false, "error": "User not found" }
→ Consumer harus parse body untuk tahu sukses atau gagal
→ Tidak bisa memanfaatkan HTTP infrastructure (cache, proxy, monitoring)
Kapan menggunakan REST: public API, mobile backend, web backend, integrasi dengan pihak ketiga, sistem yang butuh cacheability. REST adalah pilihan default yang aman untuk mayoritas use case.
Keterbatasan: over-fetching (response berisi data yang tidak semua dibutuhkan) dan under-fetching (butuh multiple request untuk mendapatkan data terkait).
GraphQL #
GraphQL dikembangkan Facebook secara internal pada 2012 untuk mengatasi masalah nyata yang mereka hadapi: news feed yang membutuhkan data dari banyak sumber berbeda, dengan mobile app yang memiliki bandwidth terbatas dan tidak mau menerima data yang tidak diperlukan.
sequenceDiagram
participant C as Client
participant G as GraphQL API
participant S as Services/DB
Note over C,G: Consumer menentukan data yang dibutuhkan
C->>G: query { user(id: "123") { name, email, orders { total } } }
G->>S: Fetch user + orders
S-->>G: Data
G-->>C: { user: { name: "Budi", email: "...", orders: [...] } }
Note over C,G: Hanya data yang diminta yang dikembalikanKarakteristik GraphQL:
→ Single endpoint: POST /graphql (semua operasi lewat sini)
→ Query-driven: consumer mendefinisikan shape response
→ Strongly typed: schema mendefinisikan semua type yang tersedia
→ Introspectable: consumer bisa query schema API itu sendiri
Contoh query:
query {
user(id: "123") {
name
email
recentOrders(limit: 5) {
id
total
status
}
}
}
→ Client hanya mendapat name, email, dan 5 order terakhir
→ Bukan seluruh profil user + seluruh riwayat order
Kapan menggunakan GraphQL: frontend-heavy application dengan kebutuhan data yang beragam antar komponen, mobile app dengan bandwidth terbatas, produk yang punya banyak tipe client berbeda (web, iOS, Android) dengan kebutuhan data yang berbeda.
Keterbatasan: server-side complexity meningkat (resolver, N+1 query problem, schema management), caching lebih sulit karena semua lewat POST, overkill untuk API yang simple.
gRPC #
gRPC dikembangkan Google dan dirancang dari awal untuk satu use case spesifik: komunikasi antar service dengan performa tinggi. Ia menggunakan Protocol Buffers (Protobuf) sebagai format serialisasi — binary, bukan text — dan HTTP/2 sebagai transport.
sequenceDiagram
participant A as Service A
participant B as Service B
Note over A,B: Komunikasi binary via HTTP/2
A->>B: GetUser(UserRequest{id: "123"}) [binary]
B-->>A: UserResponse{name: "Budi", ...} [binary]
Note over A,B: Jauh lebih kecil dan lebih cepat dari JSON/HTTPContoh .proto file (kontrak gRPC):
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
}
message GetUserRequest {
string id = 1;
}
message UserResponse {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
Keunggulan gRPC vs REST (performa):
Payload size: Protobuf ~3–10x lebih kecil dari JSON
Serialization: Binary ~5–7x lebih cepat dari JSON parsing
Connection: HTTP/2 multiplexing, satu koneksi untuk banyak request
Streaming: Native support untuk server-streaming, client-streaming, bidirectional
Kapan menggunakan gRPC: komunikasi service-to-service di microservices, sistem dengan throughput sangat tinggi, use case yang butuh streaming (log streaming, real-time data feed), internal backend yang tidak perlu diakses langsung oleh browser.
Keterbatasan: tidak human-readable (binary), kurang cocok untuk public API, browser support terbatas (butuh gRPC-Web proxy), tooling debugging lebih kompleks.
gRPC bukan pengganti REST — ia adalah alat untuk use case yang berbeda. Gunakan REST untuk API yang dikonsumsi oleh browser atau pihak eksternal, dan gRPC untuk komunikasi internal antar service yang membutuhkan performa tinggi.
JSON-RPC #
JSON-RPC adalah evolusi dari RPC klasik yang menggunakan JSON sebagai format data. Berbeda dari REST yang resource-oriented, JSON-RPC bersifat procedure-oriented: kamu memanggil fungsi dengan nama eksplisit, bukan beroperasi pada resource.
Format JSON-RPC request:
{
"jsonrpc": "2.0",
"method": "user.getById",
"params": { "id": "123" },
"id": 1
}
Format JSON-RPC response (sukses):
{
"jsonrpc": "2.0",
"result": { "id": "123", "name": "Budi" },
"id": 1
}
Format JSON-RPC response (error):
{
"jsonrpc": "2.0",
"error": { "code": -32601, "message": "Method not found" },
"id": 1
}
Kapan menggunakan JSON-RPC: legacy system integration, internal tooling yang simpel, use case di mana procedure-call model lebih natural dari resource model (misalnya: blockchain.sendTransaction, calculator.compute). JSON-RPC digunakan secara luas di protokol Web3 dan Ethereum JSON-RPC API.
Keterbatasan: tidak memanfaatkan HTTP semantics (tidak ada meaningful status code, tidak cache-friendly), tidak ada standar URL structure, kurang cocok untuk public API modern.
tRPC #
tRPC muncul dari kebutuhan spesifik ekosistem TypeScript: developer fullstack yang ingin type safety end-to-end antara backend dan frontend tanpa harus mendefinisikan schema secara manual dua kali.
Masalah yang dipecahkan tRPC:
Tanpa tRPC (REST + TypeScript):
Backend: mendefinisikan interface User { id: string; name: string }
Dokumentasi API: mendefinisikan lagi di OpenAPI spec
Frontend: mendefinisikan lagi type User { id: string; name: string }
→ Tiga tempat untuk definisi yang sama → mudah out of sync
Dengan tRPC:
Backend mendefinisikan router dengan TypeScript
Frontend langsung menggunakan type yang sama via type inference
→ Zero schema duplication, type safety end-to-end
// Backend (server)
const userRouter = router({
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return await db.users.findById(input.id);
// Return type otomatis di-infer
}),
});
// Frontend (client) — type sudah tersedia tanpa manual definition
const user = await trpc.user.getById.query({ id: "123" });
// TypeScript tahu user.name, user.email, dll — tanpa manual type definition
Kapan menggunakan tRPC: fullstack TypeScript application (Next.js + Node.js), monorepo di mana frontend dan backend ada dalam satu codebase, tim yang ingin developer experience yang sangat smooth tanpa overhead schema management.
Keterbatasan: hanya untuk ekosistem TypeScript (language-agnostic tidak mungkin), tidak cocok untuk public API (consumer tidak bisa dari bahasa lain), vendor lock-in ke framework tRPC.
Perbandingan dan Panduan Memilih #
flowchart TD
Start([Saya perlu membuat API]) --> Q1{Dikonsumsi\noleh siapa?}
Q1 -->|Pihak eksternal /\npublik| Q2{Butuh query\nfleksibel?}
Q1 -->|Internal service\nke service| Q3{Butuh performa\ndan streaming?}
Q1 -->|Frontend TypeScript\ndalam satu repo| tRPC[tRPC]
Q2 -->|Ya, frontend\nbanyak variasi| GraphQL[GraphQL]
Q2 -->|Tidak, query\nrelatif standar| REST[REST]
Q3 -->|Ya, throughput\ntinggi| gRPC[gRPC]
Q3 -->|Tidak, tool/\nlegacy integration| JSONRPC[JSON-RPC]
style REST fill:#27AE60,color:#fff,stroke:#1E8449
style GraphQL fill:#E91E8C,color:#fff,stroke:#C2185B
style gRPC fill:#4285F4,color:#fff,stroke:#2C6FAC
style JSONRPC fill:#FF9800,color:#fff,stroke:#F57C00
style tRPC fill:#2596BE,color:#fff,stroke:#1A7A9E| Kriteria | REST | GraphQL | gRPC | JSON-RPC | tRPC |
|---|---|---|---|---|---|
| Cocok untuk | Public API, Web/Mobile backend | Frontend-heavy, Multi-client | Internal microservices | Legacy, Internal tool | Fullstack TypeScript |
| Format data | JSON (biasanya) | JSON | Protobuf (binary) | JSON | JSON |
| Protokol | HTTP/1.1+ | HTTP/1.1+ | HTTP/2 | HTTP | HTTP |
| Type safety | Manual / OpenAPI | Schema-based | Protobuf schema | Tidak | End-to-end otomatis |
| Caching | Mudah (HTTP cache) | Sulit (semua POST) | Sulit | Sulit | Sedang |
| Browser support | Native | Native | Butuh proxy | Native | Native |
| Learning curve | Rendah | Sedang | Tinggi | Rendah | Rendah (jika TypeScript) |
| Dokumentasi | OpenAPI/Swagger | Introspection | .proto file | Manual | Type inference |
Anti-Pattern API yang Harus Dihindari #
// ✗ Anti-pattern 1: Verb di URL (REST)
POST /api/createUser
POST /api/deleteUser/123
GET /api/getUserById?id=123
// ✓ Solusi: Noun-based URL, HTTP method sebagai verb
POST /api/users
DELETE /api/users/123
GET /api/users/123
// ✗ Anti-pattern 2: Membocorkan detail internal
GET /api/mysql_users?table=users&limit=10
Response: { "mysql_row_id": 123, "db_created_at": 1706352000 }
// ✓ Solusi: Abstraksi yang bersih
GET /api/users?limit=10
Response: { "id": "usr_123", "created_at": "2024-01-27T10:00:00Z" }
// ✗ Anti-pattern 3: Selalu return HTTP 200
HTTP 200 OK
{ "success": false, "error": "Unauthorized", "code": 401 }
// ✓ Solusi: HTTP status code yang semantically correct
HTTP 401 Unauthorized
{ "error": { "code": "UNAUTHORIZED", "message": "Token tidak valid" } }
// ✗ Anti-pattern 4: Breaking change tanpa versioning
Mengubah field "user_name" menjadi "name" langsung di v1
→ Semua consumer yang menggunakan "user_name" langsung rusak
// ✓ Solusi: Versioning atau deprecation period
/api/v1/users → tetap mengembalikan "user_name" (deprecated)
/api/v2/users → mengembalikan "name" (baru)
Beri deprecation notice dan migration period yang cukup
// ✗ Anti-pattern 5: Tidak ada dokumentasi kontrak
"Lihat di kode saja untuk tahu formatnya"
// ✓ Solusi: Kontrak yang eksplisit dan terdokumentasi
REST: OpenAPI / Swagger spec
GraphQL: SDL (Schema Definition Language) yang bisa di-introspect
gRPC: .proto file yang di-commit ke repository
Checklist API yang Sehat #
DESAIN KONTRAK:
□ Kontrak API didefinisikan sebelum implementasi (contract-first)
□ Nama endpoint, parameter, dan field eksplisit dan self-explanatory
□ Konsistensi dijaga: naming convention, error format, response format
□ Detail internal tidak bocor ke kontrak
VERSIONING DAN COMPATIBILITY:
□ Strategi versioning didefinisikan sebelum API di-release
□ Breaking change tidak dilakukan tanpa versioning
□ Deprecation period yang cukup diberikan sebelum endpoint dihapus
□ Changelog diperbarui setiap ada perubahan
DOKUMENTASI:
□ Dokumentasi disinkronkan dengan implementasi (ideally auto-generated)
□ Setiap endpoint punya deskripsi, parameter, dan contoh response
□ Error code dan maknanya terdokumentasi
□ Authentication dan authorization requirements jelas
KEAMANAN:
□ Authentication diterapkan di semua endpoint yang membutuhkan
□ Authorization diperiksa di level endpoint (bukan hanya di middleware)
□ Input divalidasi sebelum diproses
□ Rate limiting diterapkan (terutama untuk public API)
□ Sensitive data tidak dikembalikan dalam response yang tidak perlu
PEMILIHAN TIPE API:
□ REST untuk public API dan majority use case
□ GraphQL hanya jika ada kebutuhan nyata untuk query fleksibilitas
□ gRPC hanya untuk internal service-to-service dengan kebutuhan performa
□ tRPC hanya untuk fullstack TypeScript dalam satu codebase
□ Pilihan didasarkan pada kebutuhan, bukan hype
Ringkasan #
- API adalah kontrak, bukan implementasi — implementasi boleh berubah, kontrak tidak (atau sejarang mungkin). Selalu desain API dari perspektif consumer, bukan dari perspektif implementasi internal.
- Contract-first adalah prinsip terpenting — definisikan kontrak sebelum menulis kode. Ini memungkinkan consumer dan provider bekerja paralel dan catching breaking change lebih awal.
- Abstraksi melindungi kedua belah pihak — consumer tidak perlu tahu detail internal, dan provider bebas mengubah implementasi tanpa breaking consumer.
- Konsistensi adalah investasi — API yang konsisten menurunkan cognitive load consumer secara drastis. Naming, error format, dan behavior harus predictable.
- Backward compatibility adalah tanggung jawab — setiap breaking change membutuhkan koordinasi. Versioning adalah alat, bukan solusi magic; pencegahan lebih baik dari versioning.
- REST bukan satu-satunya pilihan, tapi sering pilihan terbaik — untuk majority use case (public API, web/mobile backend), REST adalah pilihan yang paling mudah dipahami, paling banyak tooling-nya, dan paling cache-friendly.
- GraphQL untuk fleksibilitas query, bukan untuk semua API — GraphQL menambah server complexity yang signifikan. Gunakan hanya jika ada kebutuhan nyata untuk query fleksibel dari multiple client berbeda.
- gRPC untuk performa service-to-service — binary protocol dan HTTP/2 memberikan keunggulan performa yang signifikan, tapi dengan tradeoff di tooling, debugging, dan browser support.
- tRPC untuk developer experience fullstack TypeScript — type safety end-to-end tanpa schema duplication adalah keunggulan nyata, tapi hanya relevan dalam ekosistem TypeScript monorepo.
- Pilih berdasarkan kebutuhan, bukan hype — teknologi API yang tepat adalah yang paling sesuai dengan konteks: siapa consumer-nya, apa kebutuhannya, dan apa constraint-nya.
Berikutnya: REST →