REST #

REST adalah architectural style yang sudah bertahan lebih dari dua dekade dan masih menjadi pilihan utama untuk membangun API di sebagian besar sistem modern. Tapi “bertahan” bukan berarti semua orang memahaminya dengan benar — banyak API yang mengklaim RESTful tapi sebenarnya hanya “HTTP + JSON” tanpa benar-benar menerapkan prinsip-prinsip yang membuat REST kuat. Akibatnya, API yang seharusnya mudah diprediksi dan konsisten malah penuh inkonsistensi, status code yang salah, URL yang tidak masuk akal, dan breaking change yang tidak terduga. Artikel ini membahas REST dari filosofinya yang asli: apa constraint-constraint yang mendefinisikannya, mengapa constraint itu ada, bagaimana menerapkannya dengan benar, dan kapan justru REST bukan pilihan yang tepat.

Sejarah dan Filosofi REST #

REST diperkenalkan oleh Roy Fielding dalam disertasinya tahun 2000 berjudul Architectural Styles and the Design of Network-based Software Architectures. Fielding adalah salah satu kontributor utama spesifikasi HTTP/1.1 — dan REST lahir dari pengamatannya tentang mengapa web bisa berkembang ke skala global yang tidak pernah terbayangkan sebelumnya.

Insight paling penting dari Fielding: web berhasil berskala bukan hanya karena teknologinya, melainkan karena arsitekturnya — cara komponen berinteraksi, cara state dikelola, cara resource diidentifikasi. REST adalah formalisasi dari prinsip-prinsip arsitektur tersebut.

Tiga hal penting yang sering disalahpahami tentang REST:

REST bukan protokol → ia adalah architectural style
REST bukan standar formal → ia adalah sekumpulan constraint
HTTP bukan bagian dari definisi REST → HTTP adalah implementasi paling natural
                                       karena secara desain selaras dengan REST

Implikasinya: bisa ada sistem RESTful yang tidak menggunakan HTTP (walaupun sangat jarang dalam praktik), dan bisa ada API yang menggunakan HTTP tapi tidak RESTful sama sekali.


Enam Constraint REST #

Roy Fielding mendefinisikan REST melalui enam constraint. Sebuah sistem baru bisa disebut “RESTful” jika memenuhi semua constraint wajib ini.

flowchart TD
    REST["RESTful System"]

    CS["1. Client-Server\nPemisahan concern:\nUI vs Data & Logic"]
    SL["2. Stateless\nSetiap request self-contained\nTidak ada session di server"]
    CA["3. Cacheable\nResponse mendefinisikan\ncacheability-nya"]
    UI["4. Uniform Interface\nKontrak yang konsisten\ndan seragam"]
    LS["5. Layered System\nClient tidak tahu\nseberapa banyak layer"]
    COD["6. Code on Demand\n(Opsional)\nServer kirim executable code"]

    REST --> CS
    REST --> SL
    REST --> CA
    REST --> UI
    REST --> LS
    REST --> COD

    style REST fill:#4A90D9,color:#fff,stroke:#2C6FAC
    style COD fill:#95A5A6,color:#fff,stroke:#7F8C8D

1. Client-Server #

Client dan server dipisahkan secara jelas — client bertanggung jawab atas UI dan user experience, server bertanggung jawab atas data dan logika bisnis. Keduanya bisa berkembang secara independen.

Manfaat pemisahan client-server:
  ✓ Mobile app, web app, dan third-party consumer bisa menggunakan API yang sama
  ✓ Server bisa di-scale secara independen dari client
  ✓ Tim frontend dan backend bisa bekerja paralel berdasarkan kontrak API
  ✓ Client bisa diganti (misal: dari web ke mobile) tanpa mengubah server

2. Stateless #

Setiap request harus membawa semua informasi yang dibutuhkan untuk diproses. Server tidak menyimpan state dari request sebelumnya — tidak ada session, tidak ada konteks yang tersimpan di server antar request.

// ANTI-PATTERN: Stateful server
POST /api/login       → server menyimpan session di memori
GET  /api/profile     → server mencari session untuk tahu siapa yang request
→ Masalah: tidak bisa di-scale horizontal (session ada di satu server)
→ Masalah: jika server restart, semua session hilang

// BENAR: Stateless dengan token
POST /api/auth/login  → server mengembalikan JWT token
GET  /api/profile
  Authorization: Bearer eyJhbGci...
→ Token membawa semua informasi yang dibutuhkan server
→ Request bisa ditangani oleh server manapun dalam cluster

Stateless adalah constraint yang paling langsung memengaruhi skalabilitas. Sistem stateless bisa di-scale horizontal dengan mudah — tambah server baru tanpa khawatir tentang sinkronisasi state.

3. Cacheable #

Response harus secara eksplisit mendefinisikan apakah ia bisa di-cache atau tidak, berapa lama, dan dalam kondisi apa cache harus diinvalidasi. HTTP menyediakan mekanisme caching yang kaya: Cache-Control, ETag, Last-Modified, Expires.

Contoh response headers untuk caching:

GET /api/products/catalog
Cache-Control: public, max-age=3600   → bisa di-cache selama 1 jam
ETag: "abc123def456"                  → identifier versi konten

GET /api/users/123/profile
Cache-Control: private, max-age=300  → hanya di-cache di client, 5 menit
Vary: Authorization                  → cache berbeda per user

POST /api/orders
Cache-Control: no-store              → jangan di-cache sama sekali

Dengan caching yang benar, banyak request tidak perlu sampai ke server — CDN, proxy, atau browser bisa melayaninya langsung. Ini yang memungkinkan public API berskala besar melayani jutaan request per hari.

4. Uniform Interface #

Ini adalah constraint yang paling signifikan dan paling sering dilanggar. Uniform interface berarti cara berinteraksi dengan semua resource mengikuti pola yang sama dan konsisten. Fielding mendefinisikannya melalui empat sub-constraint:

a. Identifikasi resource melalui URI
   Setiap resource punya identifier unik yang tidak berubah:
   /users/123, /orders/abc, /products/xyz-001

b. Manipulasi resource melalui representasi
   Client mengirim representasi resource (JSON/XML) untuk mengubah state-nya
   Client tidak perlu tahu struktur internal server

c. Pesan bersifat self-descriptive
   Setiap request dan response membawa cukup informasi untuk dipahami:
   Content-Type: application/json, HTTP status code yang tepat, dll

d. HATEOAS (Hypermedia as the Engine of Application State)
   Response berisi link ke operasi yang relevan berikutnya
   (jarang diterapkan penuh di praktik, tapi prinsipnya penting)

5. Layered System #

Client tidak perlu tahu apakah ia terhubung langsung ke server origin atau melalui lapisan antara — load balancer, API gateway, CDN, proxy cache, atau security layer. Setiap layer hanya tahu layer yang langsung berinteraksi dengannya.

flowchart LR
    Client["Client"]
    CDN["CDN / Edge Cache"]
    GW["API Gateway\n(Auth, Rate Limit)"]
    LB["Load Balancer"]
    S1["Server 1"]
    S2["Server 2"]
    S3["Server 3"]

    Client -->|Request| CDN
    CDN -->|Cache miss| GW
    GW -->|Verified request| LB
    LB --> S1
    LB --> S2
    LB --> S3

    style Client fill:#2C3E50,color:#fff
    style CDN fill:#27AE60,color:#fff
    style GW fill:#E67E22,color:#fff
    style LB fill:#8E44AD,color:#fff

Layered system memungkinkan penambahan infrastructure concern (security, caching, logging, rate limiting) tanpa mengubah API contract.

6. Code on Demand (Opsional) #

Server dapat mengirimkan executable code ke client — JavaScript di browser adalah contoh paling umum. Ini adalah satu-satunya constraint yang opsional, dan jarang relevan untuk API modern yang dikonsumsi oleh non-browser client.


Resource-Oriented Design #

Inti dari REST adalah berpikir dalam resource, bukan dalam aksi. Resource adalah entitas yang memiliki identitas — user, order, product, invoice. Operasi terhadap resource dilakukan melalui HTTP method, bukan melalui verb di URL.

URL sebagai Identifier Resource #

// ✗ Verb di URL (bukan REST):
GET  /api/getUser?id=123
POST /api/createOrder
POST /api/deleteProduct/456
GET  /api/fetchOrdersByUser?userId=789
POST /api/updateUserStatus

// ✓ Resource-oriented URL:
GET    /api/users/123
POST   /api/orders
DELETE /api/products/456
GET    /api/users/789/orders
PATCH  /api/users/123/status

HTTP Method sebagai Operasi #

GET    → Membaca resource (safe, idempotent)
POST   → Membuat resource baru (tidak idempotent)
PUT    → Mengganti resource sepenuhnya (idempotent)
PATCH  → Mengupdate sebagian resource (idempotent jika diimplementasikan benar)
DELETE → Menghapus resource (idempotent)

Idempotent = hasil yang sama jika dilakukan berkali-kali
  GET   /users/123 → selalu mengembalikan data user 123
  DELETE /users/123 → pertama: hapus. Kedua, ketiga: 404. Tidak ada efek tambahan.
  POST   /users    → setiap call membuat user baru → TIDAK idempotent

URL Naming Convention #

Aturan yang konsisten:
  ✓ Gunakan plural noun: /users, /orders, /products (bukan /user, /order)
  ✓ Gunakan lowercase: /product-categories (bukan /ProductCategories)
  ✓ Gunakan kebab-case untuk multi-kata: /order-items (bukan /order_items atau /orderItems)
  ✓ Resource bersarang untuk relasi: /users/123/orders/456
  ✓ Query parameter untuk filter/sort/pagination: /orders?status=paid&sort=created_at

  ✗ Jangan: Verb di URL (/createUser, /deleteOrder)
  ✗ Jangan: Format file di URL (/users.json)
  ✗ Jangan: Terlalu dalam nested: /users/123/orders/456/items/789/discounts
           → lebih baik: /order-items/789 atau /orders/456/items/789

HTTP Status Code yang Benar #

Penggunaan HTTP status code yang tepat adalah bagian dari uniform interface — setiap response membawa informasi yang cukup untuk dipahami tanpa harus membaca body.

2xx — Sukses
  200 OK              → Request berhasil, ada data dikembalikan
  201 Created         → Resource baru berhasil dibuat
  202 Accepted        → Request diterima, diproses async (belum selesai)
  204 No Content      → Berhasil, tidak ada data dikembalikan (DELETE biasanya)

3xx — Redirect
  301 Moved Permanently → Resource pindah permanen (update bookmark)
  304 Not Modified      → Cache masih valid (sama dengan ETag yang ada)

4xx — Error dari Client
  400 Bad Request       → Request malformed atau parameter tidak valid
  401 Unauthorized      → Belum autentikasi (token tidak ada atau tidak valid)
  403 Forbidden         → Sudah autentikasi tapi tidak punya akses
  404 Not Found         → Resource tidak ditemukan
  405 Method Not Allowed→ HTTP method tidak didukung untuk resource ini
  409 Conflict          → Konflik state (duplicate, version mismatch)
  422 Unprocessable     → Validasi bisnis gagal (input valid secara format, tapi logika gagal)
  429 Too Many Requests → Rate limit terlampaui

5xx — Error dari Server
  500 Internal Server Error → Error tak terduga di server
  502 Bad Gateway           → Upstream server error
  503 Service Unavailable   → Server sedang tidak tersedia (maintenance/overload)
  504 Gateway Timeout       → Upstream server timeout
// ANTI-PATTERN: Selalu return 200
HTTP 200 OK
{
  "success": false,
  "error": "User not found",
  "code": 404
}
→ Client harus selalu parse body untuk tahu sukses/gagal
→ Monitoring tidak bisa mendeteksi error dari status code
→ Tidak bisa memanfaatkan HTTP infrastructure (retry logic, circuit breaker)

// BENAR: Status code yang semantically correct
HTTP 404 Not Found
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "User dengan id 123 tidak ditemukan"
  }
}
Perbedaan 401 dan 403 sering dikacaukan. 401 Unauthorized artinya “saya tidak tahu kamu siapa” — autentikasi diperlukan atau token tidak valid. 403 Forbidden artinya “saya tahu kamu siapa, tapi kamu tidak boleh melakukan ini” — autentikasi berhasil tapi tidak ada otorisasi. Menggunakan 403 saat token tidak ada (harusnya 401) adalah kesalahan yang umum.

Format Response yang Konsisten #

Konsistensi format response adalah hal yang sering diremehkan tapi sangat memengaruhi pengalaman developer yang menggunakan API.

Format response sukses (200, 201):

// Single resource
GET /api/users/123
{
  "data": {
    "id": "usr_123",
    "name": "Budi Santoso",
    "email": "[email protected]",
    "created_at": "2024-01-15T10:00:00Z"
  }
}

// Collection dengan pagination
GET /api/users?page=2&limit=20
{
  "data": [...],
  "meta": {
    "page": 2,
    "per_page": 20,
    "total": 150,
    "total_pages": 8
  }
}

Format response error (4xx, 5xx):
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Input tidak valid",
    "details": [
      { "field": "email", "message": "Format email tidak valid" },
      { "field": "phone", "message": "Nomor telepon wajib diisi" }
    ]
  }
}

Tiga aturan yang perlu dijaga konsistensinya:

  • data untuk payload sukses
  • meta untuk pagination dan metadata
  • error untuk semua kondisi error — dengan code (machine-readable) dan message (human-readable)

Pagination, Filtering, dan Sorting #

API yang mengembalikan semua data sekaligus adalah API yang akan menjadi masalah saat data tumbuh. Pagination, filtering, dan sorting harus dirancang dari awal.

Pagination #

// Offset-based pagination (paling umum):
GET /api/orders?page=3&per_page=20
Response:
{
  "data": [...],
  "meta": {
    "page": 3,
    "per_page": 20,
    "total": 547,
    "total_pages": 28,
    "has_next": true,
    "has_prev": true
  }
}

// Cursor-based pagination (lebih baik untuk real-time data):
GET /api/orders?cursor=eyJpZCI6MTIzfQ&limit=20
Response:
{
  "data": [...],
  "meta": {
    "next_cursor": "eyJpZCI6MTQzfQ",
    "prev_cursor": "eyJpZCI6MTAzfQ",
    "has_next": true
  }
}

Cursor-based lebih baik untuk data yang berubah cepat — offset-based bisa “melewatkan” atau “menduplikasi” data jika ada insert/delete di antara halaman.

Filtering dan Sorting #

// Filtering:
GET /api/orders?status=paid&user_id=123
GET /api/products?category=electronics&min_price=100&max_price=500

// Sorting (prefix minus untuk descending):
GET /api/orders?sort=-created_at         → terbaru dulu
GET /api/orders?sort=total,-created_at  → urutkan by total ASC, lalu created_at DESC

// Field selection (mengurangi over-fetching):
GET /api/users/123?fields=id,name,email
→ Hanya mengembalikan field yang diminta

Versioning Strategy #

Setiap API yang digunakan orang lain akan perlu berubah. Versioning adalah cara untuk melakukan perubahan breaking tanpa merusak consumer yang sudah ada.

Tiga pendekatan versioning:

1. URL path versioning (paling umum dan paling jelas):
   /api/v1/users
   /api/v2/users
   ✓ Terlihat jelas di URL
   ✓ Mudah ditest di browser
   ✗ "Mengotori" URL

2. Header versioning:
   GET /api/users
   Accept: application/vnd.myapi.v2+json
   ✓ URL tetap bersih
   ✗ Tidak terlihat di URL, lebih sulit debug

3. Query parameter versioning:
   GET /api/users?version=2
   ✓ Mudah untuk testing
   ✗ Bisa ter-cache tanpa versi yang benar

Rekomendasi:
  → Gunakan URL path versioning untuk majority use case
  → Mulai dari /api/v1 sejak hari pertama, meski belum ada rencana v2
  → Pertahankan versi lama minimal 6–12 bulan setelah v2 rilis
  → Komunikasikan deprecation schedule jauh-jauh hari
// ANTI-PATTERN: Breaking change tanpa versioning
Sprint 5: GET /api/users/123 → { "user_name": "Budi" }
Sprint 8: GET /api/users/123 → { "name": "Budi" }  ← field name berubah
→ Semua consumer yang pakai "user_name" langsung rusak

// BENAR: Versioning untuk breaking change
/api/v1/users/123 → { "user_name": "Budi" }  ← tetap berjalan
/api/v2/users/123 → { "name": "Budi" }        ← perubahan di versi baru
Deprecation notice di v1 response headers:
  Deprecation: true
  Sunset: Sat, 01 Jan 2027 00:00:00 GMT
  Link: <https://api.example.com/v2/users>; rel="successor-version"

Menangani Operasi Non-CRUD #

REST paling natural untuk operasi CRUD (Create, Read, Update, Delete). Tapi tidak semua operasi bisa dipetakan langsung ke CRUD. Beberapa pendekatan untuk operasi yang lebih kompleks:

Pendekatan 1: Modelkan aksi sebagai resource (paling RESTful)
  POST /api/orders/123/cancellation     → membuat resource "cancellation"
  POST /api/users/123/password-reset    → membuat resource "password reset"
  POST /api/payments/123/refund         → membuat resource "refund"

Pendekatan 2: Gunakan sub-resource dengan verba yang jelas
  POST /api/orders/123/cancel           → aksi eksplisit pada resource
  POST /api/emails/123/send             → mengirim email

Pendekatan 3: Untuk operasi batch atau bulk
  POST /api/users/batch                 → buat banyak user sekaligus
  Body: { "users": [...] }

  PATCH /api/orders                     → update banyak order sekaligus
  Body: { "ids": [1,2,3], "status": "shipped" }

Yang sebaiknya dihindari:
  ✗ POST /api/doSomething               → verb di URL
  ✗ GET  /api/runJob                    → side effect di GET

Request dan Response Best Practice #

Request Headers yang Penting #

Content-Type: application/json           → format body yang dikirim
Accept: application/json                 → format response yang diinginkan
Authorization: Bearer <token>            → autentikasi
X-Request-ID: uuid-v4                   → tracing (opsional tapi sangat berguna)
X-Idempotency-Key: unique-key           → untuk operasi yang butuh idempotency

Response Headers yang Penting #

Content-Type: application/json; charset=utf-8
X-Request-ID: <echo dari request atau generated baru>
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: 1706352000
Cache-Control: private, max-age=300

Idempotency untuk Operasi Penting #

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: Skenario: request POST gagal di tengah jalan

    C->>S: POST /api/orders\nX-Idempotency-Key: key-abc123\nBody: { items: [...] }
    S-->>C: ❌ Network timeout — client tidak tahu apakah order terbuat

    Note over C,S: Client retry dengan key yang sama
    C->>S: POST /api/orders\nX-Idempotency-Key: key-abc123\nBody: { items: [...] }
    S-->>C: 200 OK (bukan 201) — order yang sama dikembalikan\ntanpa membuat duplikat

REST vs SOAP vs GraphQL vs gRPC #

SOAP:
  Format: XML only, berat
  Kompleksitas: Tinggi (WSDL, WS-Security, dll)
  Use case: Enterprise legacy, banking, insurance yang butuh formal contract
  Kapan dipilih: Saat terpaksa integrate dengan sistem lama

REST:
  Format: JSON (biasanya), fleksibel
  Kompleksitas: Rendah
  Use case: Public API, web/mobile backend, majority use case
  Kapan dipilih: Default choice untuk hampir semua API baru

GraphQL:
  Format: JSON
  Kompleksitas: Sedang (server lebih kompleks, client lebih fleksibel)
  Use case: Frontend-heavy dengan banyak variasi query
  Kapan dipilih: Ada kebutuhan nyata untuk query fleksibel dari multi-client

gRPC:
  Format: Binary (Protobuf)
  Kompleksitas: Tinggi
  Use case: Internal service-to-service, high throughput
  Kapan dipilih: Performa jadi constraint utama dan semua service internal

Anti-Pattern REST yang Harus Dihindari #

// ✗ Anti-pattern 1: Verb di URL
POST /api/createUser
GET  /api/getUserById?id=123
POST /api/deleteOrder/456

// ✓ Solusi: Noun di URL, HTTP method sebagai verb
POST   /api/users
GET    /api/users/123
DELETE /api/orders/456

---

// ✗ Anti-pattern 2: Selalu return 200
HTTP 200 OK
{ "error": true, "message": "Not found" }

// ✓ Solusi: Status code yang semantically correct
HTTP 404 Not Found
{ "error": { "code": "NOT_FOUND", "message": "..." } }

---

// ✗ Anti-pattern 3: Response format yang inkonsisten
GET /users/1    → { "userId": 1, "userName": "Budi" }
GET /orders/1   → { "id": 1, "order_user": "Budi" }
GET /products/1 → { "productId": 1, "product_name": "..." }

// ✓ Solusi: Satu konvensi untuk semua resource
Semua pakai: { "id": ..., snake_case untuk semua field }

---

// ✗ Anti-pattern 4: Breaking change tanpa versioning
Field "email" dihapus dari response langsung
→ Consumer yang menggunakan "email" langsung rusak

// ✓ Solusi: Versioning + deprecation period
Tambahkan di v2, pertahankan v1 dengan deprecation notice

---

// ✗ Anti-pattern 5: Endpoint yang terlalu granular atau terlalu generik
Terlalu granular:
  GET /api/user-first-name/123
  GET /api/user-last-name/123
  GET /api/user-email/123
  → Butuh banyak request untuk data satu user

Terlalu generik:
  POST /api/action
  Body: { "type": "create_user", "data": {...} }
  → Menghilangkan semua keuntungan REST

// ✓ Solusi: Resource yang granularitasnya tepat
GET /api/users/123
→ Satu request, data user lengkap

Kapan Tidak Menggunakan REST #

REST bukan solusi untuk semua masalah. Ada situasi di mana pendekatan lain lebih tepat:

Gunakan WebSocket jika:
  ✓ Butuh real-time bidirectional communication
  ✓ Chat, collaborative editing, live dashboard, gaming
  → REST request-response tidak efisien untuk ini

Gunakan SSE (Server-Sent Events) jika:
  ✓ Butuh server push ke client (satu arah)
  ✓ Live notifications, progress tracking
  → REST polling terlalu mahal

Gunakan gRPC jika:
  ✓ Internal service-to-service communication
  ✓ High throughput, performa jadi constraint utama
  ✓ Streaming data
  → REST overhead terlalu tinggi untuk ini

Gunakan GraphQL jika:
  ✓ Frontend punya kebutuhan data yang sangat beragam
  ✓ Multiple client berbeda (web, mobile, widget) butuh data berbeda
  ✓ Over-fetching sudah jadi masalah yang nyata dan terukur
  → REST over-fetching merugikan secara signifikan

Tetap gunakan REST jika:
  ✓ Public API yang dikonsumsi pihak eksternal
  ✓ Web atau mobile backend dengan use case standar
  ✓ Tim belum punya masalah spesifik yang membutuhkan solusi lain
  → Default yang aman dan familiar

Checklist REST API yang Sehat #

URL DAN RESOURCE DESIGN:
  □ URL menggunakan noun, bukan verb
  □ Resource names plural dan lowercase
  □ Multi-kata menggunakan kebab-case
  □ Nesting resource maksimal 2–3 level
  □ Query parameter untuk filter, sort, pagination

HTTP METHOD DAN STATUS CODE:
  □ GET hanya untuk read, tidak ada side effect
  □ POST untuk create, PUT/PATCH untuk update, DELETE untuk hapus
  □ Status code semantically correct (bukan selalu 200)
  □ 401 vs 403 dibedakan dengan benar
  □ 422 untuk validasi bisnis, 400 untuk format/parameter error

RESPONSE FORMAT:
  □ Format konsisten di semua endpoint (data, meta, error)
  □ Error response punya code (machine-readable) dan message (human-readable)
  □ Pagination info ada di meta untuk collection endpoint
  □ Timestamp dalam ISO 8601 (bukan Unix timestamp kecuali ada alasan)
  □ ID tidak membocorkan detail internal (gunakan UUID atau prefixed ID)

VERSIONING DAN COMPATIBILITY:
  □ Versioning ada sejak hari pertama (/v1/)
  □ Breaking change hanya dilakukan lewat versi baru
  □ Deprecation dikomunikasikan lewat header
  □ Versi lama dipertahankan dengan grace period yang cukup

KEAMANAN:
  □ Authentication di semua endpoint yang butuh (401 untuk unauthorized)
  □ Authorization di level resource (403 untuk forbidden)
  □ Input divalidasi sebelum diproses
  □ Rate limiting diterapkan
  □ HTTPS only di production

DOKUMENTASI:
  □ OpenAPI/Swagger spec tersedia dan sinkron dengan implementasi
  □ Semua endpoint terdokumentasi dengan contoh request dan response
  □ Error code dan artinya terdokumentasi
  □ Authentication flow terdokumentasi

Ringkasan #

  • REST adalah architectural style, bukan protokol — bukan sekadar “HTTP + JSON”. Memahami enam constraint-nya (client-server, stateless, cacheable, uniform interface, layered system, code on demand) adalah kunci membangun API yang benar-benar RESTful.
  • Stateless adalah fondasi skalabilitas — server tidak menyimpan state client memungkinkan scale horizontal tanpa koordinasi antar server. Token-based auth (JWT) adalah implementasi yang benar; session server-side melanggar constraint ini.
  • URL adalah identifier resource, HTTP method adalah operasi — gunakan noun di URL (/users/123), HTTP method sebagai verb (GET, POST, PUT, PATCH, DELETE). Verb di URL adalah anti-pattern yang paling sering ditemukan.
  • HTTP status code adalah bagian dari kontrak — selalu return status code yang semantically correct. 401 untuk belum autentikasi, 403 untuk tidak punya akses, 422 untuk validasi bisnis gagal. Mengembalikan 200 untuk semua response menghilangkan nilai dari uniform interface.
  • Konsistensi format response lebih penting dari kesempurnaan struktur — pilih satu konvensi (data/meta/error) dan terapkan konsisten di semua endpoint. Inkonsistensi memaksa consumer mempelajari setiap endpoint secara individual.
  • Versioning harus ada sejak hari pertama — mulai dari /api/v1 meski belum ada rencana perubahan. Breaking change tanpa versioning adalah pelanggaran kontrak yang merusak consumer.
  • Pagination, filtering, dan sorting harus dirancang dari awal — API yang mengembalikan semua data tanpa batas adalah API yang akan menjadi masalah saat data tumbuh.
  • Caching adalah keunggulan REST yang sering dimanfaatkan — gunakan Cache-Control, ETag, dan conditional request untuk mengurangi beban server secara signifikan, terutama untuk data yang tidak sering berubah.
  • REST bukan untuk semua use case — real-time butuh WebSocket/SSE, service-to-service throughput tinggi butuh gRPC, query fleksibel multi-client butuh GraphQL. Pilih REST karena ia tepat, bukan karena familiar.
  • Dokumentasi adalah bagian dari API, bukan pelengkap — OpenAPI spec yang sinkron dengan implementasi adalah kontrak tertulis yang memungkinkan consumer dan provider berkembang secara independen.

← Sebelumnya: Fundamental   Berikutnya: GraphQL →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact