Logo
HOMEABOUTPROJECTSBLOGCONTACT

Luthfi A. Pratama

Enterprise integration specialist building scalable, mission-critical solutions for modern businesses.

LINKS

  • About
  • Projects
  • Blog
  • Contact

CONNECT

GitHubLinkedInEmail

© 2026 Luthfi A. Pratama. All rights reserved.

Designed and built with passion.

Back to all articles
Software Engineering

REST API Design: Gimana Bikin API yang Ga Bikin Frontend Developer Nangis

Catatan pembelajaran tentang REST API - dari prinsip dasar REST, naming convention, HTTP methods, status codes, versioning, pagination, error handling, sampai best practices di production.

Luthfi A. Pratama

Luthfi A. Pratama

Software Engineer

2026-03-07
14 min read
REST API Design: Gimana Bikin API yang Ga Bikin Frontend Developer Nangis

Apa itu REST?

REST (Representational State Transfer) itu arsitektur style untuk mendesain API di web. Dibuat oleh Roy Fielding di disertasinya tahun 2000.

REST bukan protocol, bukan framework, bukan library. Dia kumpulan aturan tentang gimana client dan server berkomunikasi lewat HTTP.

6 Constraint REST

Constraint Artinya
Client-Server Client dan server terpisah, bisa berkembang sendiri-sendiri
Stateless Setiap request harus bawa semua info yang dibutuhkan. Server ga simpan state client
Cacheable Response harus bilang apakah boleh di-cache atau tidak
Uniform Interface Interface yang konsisten dan predictable
Layered System Boleh ada layer di antara client dan server (load balancer, proxy, gateway)
Code on Demand Server bisa kirim executable code ke client (opsional, jarang dipakai)

Yang paling penting: Stateless dan Uniform Interface.

Resource-Based Thinking

Kunci REST itu pikirkan resource, bukan action.

Resource itu noun, bukan verb. Contoh:

❌ Salah (action-based):
GET  /getUsers
POST /createUser
POST /deleteUser/123
GET  /getUserOrders/123

✅ Benar (resource-based):
GET    /users
POST   /users
DELETE /users/123
GET    /users/123/orders

Setiap resource punya URI (Uniform Resource Identifier) yang jelas. HTTP method yang menentukan apa yang dilakukan.

HTTP Methods

Method Fungsi Idempotent? Safe?
GET Ambil data ✅ ✅
POST Buat resource baru ❌ ❌
PUT Update keseluruhan resource ✅ ❌
PATCH Update sebagian resource ❌ ❌
DELETE Hapus resource ✅ ❌

Idempotent: kalau dipanggil berkali-kali hasilnya sama. DELETE /users/123 — panggil 1x atau 5x, user 123 tetap terhapus.

Safe: ga mengubah state server. GET cuma baca, ga ubah apa-apa.

PUT vs PATCH

// User saat ini
{
  "id": 123,
  "name": "Budi",
  "email": "budi@mail.com",
  "phone": "081234567890"
}

PUT — replace seluruh resource. Harus kirim semua field:

PUT /users/123
{
  "name": "Budi Santoso",
  "email": "budi@mail.com",
  "phone": "081234567890"
}
// Kalau ga kirim "phone", phone jadi null!

PATCH — update sebagian field aja:

PATCH /users/123
{
  "name": "Budi Santoso"
}
// Field lain tetap tidak berubah

URL Naming Convention

Rules

  1. Gunakan noun, bukan verb
✅ /users
❌ /getUsers
  1. Gunakan plural
✅ /users, /products, /orders
❌ /user, /product, /order
  1. Gunakan kebab-case
✅ /user-profiles
❌ /userProfiles, /user_profiles
  1. Hierarchy pakai nesting
✅ /users/123/orders        (orders milik user 123)
✅ /users/123/orders/456    (order 456 milik user 123)
❌ /getUserOrders?userId=123
  1. Jangan terlalu dalam nesting-nya (max 2-3 level)
✅ /users/123/orders
❌ /users/123/orders/456/items/789/reviews/101
// Better: /order-items/789/reviews
  1. Trailing slash konsisten (pilih salah satu)
✅ /users    (tanpa trailing slash - umumnya lebih dipakai)
✅ /users/   (dengan trailing slash - konsisten)
❌ kadang /users kadang /users/

HTTP Status Codes

Jangan cuma return 200 untuk semuanya. Status code itu bagian dari API contract.

2xx — Success

Code Nama Kapan Pakai
200 OK GET berhasil, atau general success
201 Created POST berhasil buat resource baru
204 No Content DELETE berhasil, ga ada body response

3xx — Redirection

Code Nama Kapan Pakai
301 Moved Permanently Resource pindah URL permanen
304 Not Modified Cache masih valid

4xx — Client Error

Code Nama Kapan Pakai
400 Bad Request Request body invalid, validation gagal
401 Unauthorized Belum login / token expired
403 Forbidden Sudah login tapi ga punya akses
404 Not Found Resource ga ada
409 Conflict Duplikat, atau state conflict
422 Unprocessable Entity Syntax benar tapi semantik salah
429 Too Many Requests Rate limit exceeded

5xx — Server Error

Code Nama Kapan Pakai
500 Internal Server Error Bug di server
502 Bad Gateway Upstream server error
503 Service Unavailable Server overload atau maintenance

401 vs 403

Ini sering ketuker:

  • 401 Unauthorized: "Siapa kamu? Gue ga kenal." (belum autentikasi)
  • 403 Forbidden: "Gue kenal kamu, tapi kamu ga boleh akses ini." (sudah autentikasi, tapi ga diotorisasi)

Error Handling

Jangan cuma return status code. Kasih error response yang berguna.

Struktur Error yang Baik

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body tidak valid",
    "details": [
      {
        "field": "email",
        "message": "Format email tidak valid",
        "value": "bukan-email"
      },
      {
        "field": "age",
        "message": "Harus lebih dari 0",
        "value": -5
      }
    ]
  }
}

Yang Harus Ada di Error Response

  1. Error code — machine-readable (untuk conditional handling di frontend)
  2. Message — human-readable (bisa ditampilin ke user)
  3. Details — per-field validation errors
  4. Timestamp — kapan error terjadi (optional, bagus buat debugging)

Yang Jangan Ada

  • Stack trace di production ❌
  • Internal database error message ❌
  • Sensitive info (password, token, SQL query) ❌
// ❌ JANGAN GINI
{
  "error": "SQL Error: SELECT * FROM users WHERE email='x'; DROP TABLE users;--"
}

// ✅ GINI
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Terjadi kesalahan server. Silakan coba lagi."
  }
}

Pagination

Ga mungkin return 1 juta rows di satu response. Harus di-page.

Offset-Based Pagination

GET /products?page=2&limit=20

Response:

{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "totalItems": 1543,
    "totalPages": 78
  }
}

Pro: simple, bisa jump ke page mana aja
Kontra: kalau data berubah di tengah (insert/delete), bisa skip atau duplikat item

Cursor-Based Pagination

GET /products?limit=20&cursor=eyJpZCI6MTAwfQ==

Response:

{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6MTIwfQ==",
    "hasMore": true
  }
}

Cursor itu encoded reference ke item terakhir. Biasanya Base64 dari {id: 100}.

Pro: consistent results walaupun data berubah, performance lebih baik di dataset besar
Kontra: ga bisa jump ke page 50 langsung. Harus sequential.

Kapan Pakai Mana?

Kebutuhan Offset Cursor
Jump ke halaman tertentu ✅ ❌
Data sering berubah (real-time feed) ❌ ✅
Dataset sangat besar ❌ (slow) ✅ (fast)
Simple implementation ✅ ❌

Filtering, Sorting, dan Searching

Filtering

Pakai query parameters:

GET /products?category=electronics&minPrice=100000&maxPrice=500000
GET /users?status=active&role=admin

Sorting

GET /products?sort=price        // ascending (default)
GET /products?sort=-price       // descending (dash prefix)
GET /products?sort=category,-price  // multi-sort

Searching

GET /products?search=laptop gaming
GET /users?q=budi

Sparse Fields (Field Selection)

Kalau cuma butuh beberapa field:

GET /users?fields=id,name,email

Response cuma isinya 3 field itu. Hemat bandwidth.

API Versioning

API pasti berubah. Gimana caranya supaya client lama ga tiba-tiba rusak?

Strategy 1: URL Path (Paling Umum)

GET /api/v1/users
GET /api/v2/users

Pro: paling explicit, mudah dipahami
Kontra: URL berubah, bisa merusak caching

Strategy 2: Header

GET /api/users
Accept: application/vnd.myapp.v2+json

Pro: URL tetap bersih
Kontra: kurang discoverable, debugging lebih susah

Strategy 3: Query Parameter

GET /api/users?version=2

Pro: simple
Kontra: optional parameter bisa di-cache salah

Rekomendasi

Pakai URL Path versioning (/api/v1/). Paling clear, paling banyak dipakai, paling gampang di-debug.

Aturan versioning:

  • Naikkan major version cuma kalau ada breaking change
  • Backward-compatible changes ga perlu new version
  • Support minimal 2 versi terakhir
  • Kasih deprecation notice sebelum matiin versi lama

Response Format

Envelope Pattern

Bungkus response dalam wrapper yang konsisten:

// Success
{
  "status": "success",
  "data": {
    "id": 123,
    "name": "Budi"
  }
}

// Success (list)
{
  "status": "success",
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalItems": 100
  }
}

// Error
{
  "status": "error",
  "error": {
    "code": "NOT_FOUND",
    "message": "User tidak ditemukan"
  }
}

Pro: frontend selalu tahu struktur response. Cek status dulu baru handle.
Kontra: sedikit redundant sama HTTP status code.

Naming Convention di JSON

Pakai camelCase (paling umum di JavaScript ecosystem):

{
  "userId": 123,
  "firstName": "Budi",
  "createdAt": "2026-03-07T10:00:00Z"
}

Jangan mix:

// ❌ Jangan gini
{
  "user_id": 123,
  "firstName": "Budi",
  "Created-At": "..."
}

Timestamp Format

Selalu pakai ISO 8601 dan UTC:

{
  "createdAt": "2026-03-07T10:00:00Z",
  "updatedAt": "2026-03-07T15:30:00Z"
}

Jangan "07 Mar 2026" atau UNIX timestamp 1741334400.

Authentication

Token-Based (JWT)

GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  • Client login → dapet token
  • Setiap request kirim token di Authorization header
  • Server verifikasi token

API Key

GET /api/products
X-API-Key: sk_live_abc123xyz
  • Biasanya untuk server-to-server communication
  • Simple, tapi kurang secure kalau expose ke client

Jangan Pernah

  • Taruh credentials di URL: GET /api/users?token=abc123 ❌ (keliatan di logs, browser history)
  • Pakai HTTP (bukan HTTPS) untuk kirim token ❌

Rate Limiting

Lindungi API dari abuse dan overload.

HTTP/1.1 429 Too Many Requests
Retry-After: 30

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1741334400

Headers yang harus ada:

Header Artinya
X-RateLimit-Limit Max requests per window
X-RateLimit-Remaining Sisa request yang bisa dilakukan
X-RateLimit-Reset Kapan window reset (UNIX timestamp)
Retry-After Berapa detik harus nunggu

Strategi umum:

  • 100 requests per menit untuk public API
  • 1000 requests per menit untuk authenticated users
  • Lebih tinggi untuk partner/premium tier

HATEOAS (Bonus)

HATEOAS (Hypermedia As The Engine Of Application State) — level tertinggi REST maturity.

Response berisi link ke action selanjutnya:

{
  "data": {
    "id": 123,
    "name": "Budi",
    "status": "active"
  },
  "_links": {
    "self": { "href": "/api/v1/users/123" },
    "orders": { "href": "/api/v1/users/123/orders" },
    "deactivate": { "href": "/api/v1/users/123/deactivate", "method": "POST" }
  }
}

Client ga perlu hardcode URL. Tinggal ikutin link dari response. Keren secara teori, tapi jarang diimplementasi penuh di real world karena complexity-nya.

Checklist: API Design Review

Sebelum release API, cek:

  • URL pakai noun, plural, kebab-case?
  • HTTP method sesuai (GET baca, POST buat, PUT update, DELETE hapus)?
  • Status code benar (bukan cuma 200 untuk semuanya)?
  • Error response ada code, message, dan details?
  • Pagination ada di semua list endpoint?
  • Sensitive data ga bocor di error response?
  • Versioning sudah diterapkan?
  • Rate limiting sudah dipasang?
  • Auth pakai header (bukan URL parameter)?
  • Timestamp pakai ISO 8601 UTC?
  • Response format konsisten di semua endpoint?
  • HTTPS only?

Ringkasan

Konsep Penjelasan
REST Arsitektur style berbasis resource + HTTP
Resource Noun-based URL (/users, /orders)
HTTP Methods GET (baca), POST (buat), PUT/PATCH (update), DELETE (hapus)
Status Codes 2xx sukses, 4xx client error, 5xx server error
Pagination Offset-based (simple) vs Cursor-based (scalable)
Versioning /api/v1/ paling umum dan recommended
Error Handling Structured error dengan code + message + details
Rate Limiting Protect API dari abuse dengan request quota
HATEOAS Response berisi links ke related actions

Kesimpulan

REST API yang bagus itu predictable. Developer yang baru pertama kali baca dokumentasi API kamu harus bisa nebak gimana endpoint lain bekerja hanya dari melihat satu contoh.

Yang perlu diingat:

  1. Think in resources — noun, bukan verb
  2. HTTP methods punya makna — jangan POST untuk semuanya
  3. Status codes bukan hiasan — 201 Created, bukan 200 OK untuk POST
  4. Error response yang berguna — jangan cuma "error occurred"
  5. Konsistensi itu segalanya — naming, format, structure harus seragam
  6. Versioning dari hari pertama — ga ada API yang ga berubah
  7. Pagination wajib — kalau ada list endpoint, harus paginasi

API itu kontrak antara frontend dan backend. Kalau kontraknya jelas, kedua tim bisa kerja paralel tanpa drama. Kalau ga, siap-siap meeting tiap hari 😅

Topics

Software Engineering

Related Articles

OAuth 2.0 & OpenID Connect: Gimana "Login dengan Google" Itu Bekerja

Catatan pembelajaran tentang OAuth 2.0 dan OpenID Connect - dari kenapa kita butuh authorization framework, Authorization Code Flow step-by-step, PKCE, ID token vs access token, sampai common mistakes di production.

Caching: Kenapa Sistem Cepat Itu Sistem yang Males Ngitung Ulang

Catatan pembelajaran tentang caching - dari kenapa cache itu penting, strategi caching, Redis vs Memcached, cache invalidation, CDN, sampai pitfalls yang sering kejadian di production.

Clustering: Gimana Banyak Server Kerja Bareng Jadi Satu Sistem

Catatan pembelajaran tentang konsep clustering - dari kenapa satu server ga cukup, gimana cluster milih leader, sampai consensus algorithm dan real-world implementations.