Desain REST API dengan Claude Code: endpoint, error, pagination, idempotensi
Rancang REST API dengan Claude Code sebelum coding: OpenAPI, error, pagination, idempotensi, dan checklist.
REST API belum selesai hanya karena beberapa route sudah dibuat. Sebelum implementasi, tentukan apa yang disebut resource, metode HTTP apa yang dipakai setiap operasi, apa respons saat gagal, bagaimana daftar dipaginasi, dan bagaimana permintaan yang sama ditangani saat dikirim berulang. Jika keputusan ini dibiarkan kabur, Claude Code tetap bisa menulis kode dengan cepat, tetapi frontend, aplikasi mobile, dan integrasi partner akan membaca kontrak yang berbeda.
Artikel ini membahas cara memakai Claude Code untuk mengunci desain REST API sebelum masuk ke kode. REST adalah singkatan dari Representational State Transfer, tetapi dalam pekerjaan sehari-hari cukup dipahami sebagai aturan untuk mengoperasikan resource seperti order, user, dan invoice melalui HTTP. Resource adalah objeknya, method adalah operasinya, dan status code adalah sinyal hasilnya.
Sebagai rujukan resmi, gunakan HTTP request methods dan HTTP response status codes dari MDN, serta OpenAPI Specification. Per 3 Juni 2026, halaman latest OpenAPI mengarah ke 3.2.0; contoh di artikel ini memakai openapi: 3.1.0 karena banyak linter dan generator masih lebih stabil dengannya.
Untuk lanjutan implementasi, baca juga pengembangan API produksi dengan Claude Code, pengujian API dengan Claude Code, dan code review Claude Code. Template dan checklist siap pakai tersedia di /products/, sedangkan pelatihan tim untuk membakukan review desain API ada di /training/.
Samakan Istilah Dulu
Pemula sering tersandung karena istilah desain REST mirip-mirip. Sebelum meminta Claude Code mengimplementasikan apa pun, pastikan tim memakai kata-kata ini dengan arti yang sama.
| Istilah | Penjelasan sederhana | Contoh |
|---|---|---|
| Resource | Kata benda yang dikelola API | orders, customers |
| Endpoint | Gabungan URL dan metode HTTP | GET /v1/orders |
| Method | Verb HTTP yang menunjukkan operasi | GET, POST, PATCH |
| Status code | Angka untuk memberi sinyal sukses, input salah, belum login, dan lainnya | 200, 201, 400, 404 |
| Validation | Pemeriksaan bahwa request berbentuk benar | Format email, jumlah minimum |
| Idempotensi | Operasi yang sama dapat diulang tanpa mengubah state akhir | Satu order untuk key yang sama |
Claude Code bagus membaca konteks, tetapi tidak bisa menjamin memahami niat desain yang belum ditulis. “Buat API pembuatan order” bisa menjadi /createOrder, /orders/create, atau POST /orders. Memberikan istilah dan aturan desain lebih dulu adalah cara paling sederhana untuk menaikkan kualitas implementasi.
Putuskan Lewat Tiga Use Case
API order yang sama punya prioritas berbeda tergantung cara dipakai.
Use case pertama adalah integrasi B2B SaaS. Jika partner mengirim ulang CSV malam yang sama, POST /v1/orders membutuhkan Idempotency-Key agar order tidak tercipta ganda. Retry sering terjadi saat pemulihan insiden, sehingga idempotensi sulit ditambahkan belakangan.
Use case kedua adalah API daftar untuk admin. Staf memfilter status=paid, pindah halaman, lalu staf lain menambahkan order. page=2 dapat bergeser. Gunakan cursor pagination dengan limit dan after, serta sort stabil seperti createdAt desc, id desc.
Use case ketiga adalah API aplikasi mobile. Jika versi lama tetap terpasang berbulan-bulan, nama field respons tidak boleh berubah tiba-tiba. Field wajib baru atau format error baru sebaiknya masuk /v2, sementara kontrak /v1 tetap ada di OpenAPI. Penambahan field opsional biasanya tidak perlu versi baru.
Use case keempat adalah API internal antar layanan. Consumer lebih mudah diperbarui bersama, tetapi monitoring dan alert sering lebih lemah. Bahkan untuk internal, status code dan format error yang tidak konsisten membuat setiap caller menulis penanganan khusus.
Lihat Alur Desainnya
Kunci desain dalam urutan ini sebelum memberikan tugas ke Claude Code. Ini membantu implementasi tetap mengikuti kontrak saat file diedit.
flowchart TD
A["Use cases"] --> B["Resources and endpoints"]
B --> C["OpenAPI contract"]
C --> D["Errors, pagination, idempotency"]
D --> E["Claude Code implementation"]
E --> F["Tests and review"]
Kunci Tabel Endpoint Sebelum Coding
Deliverable pertama bukan kode, melainkan tabel endpoint. Berikan tabel ini ke Claude Code agar routing, OpenAPI, test, dan dokumentasi dimulai dari kontrak yang sama.
| Tujuan | Method dan path | Sukses | Aturan penting |
|---|---|---|---|
| Daftar order | GET /v1/orders?status=paid&limit=20&after=ord_123 | 200 OK | Cursor pagination dengan sort stabil |
| Buat order | POST /v1/orders | 201 Created | Terima Idempotency-Key dan kembalikan Location |
| Detail order | GET /v1/orders/{orderId} | 200 OK | Jika tidak ada, kembalikan 404 |
| Update order | PATCH /v1/orders/{orderId} | 200 OK | Update sebagian; patch kosong adalah 400 |
| Batalkan order | POST /v1/orders/{orderId}/cancel | 200 OK | Perlakukan perubahan status secara konsisten |
Aturan umum: kata benda di URL, kata kerja di method. Beberapa aksi bisnis, seperti membatalkan order, lebih jelas sebagai sub-aksi eksplisit daripada dipaksa menjadi DELETE. Yang penting adalah menulis pengecualiannya dan memakai pola yang sama untuk operasi sejenis.
Jadikan OpenAPI Sumber Desain
OpenAPI menjelaskan path, parameter, request, dan response dalam bentuk yang dapat dibaca mesin. Jika Anda memberi tahu Claude Code bahwa kontrak OpenAPI ini adalah sumber kebenaran, ruang bebas implementasinya menjadi tepat.
openapi: 3.1.0
info:
title: Acme Orders API
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/v1/orders:
get:
operationId: listOrders
summary: List orders
parameters:
- $ref: "#/components/parameters/StatusParam"
- $ref: "#/components/parameters/LimitParam"
- $ref: "#/components/parameters/AfterParam"
responses:
"200":
description: Orders are returned in a stable cursor order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderListResponse"
"400":
$ref: "#/components/responses/BadRequest"
post:
operationId: createOrder
summary: Create an order
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrderRequest"
responses:
"201":
description: Order created.
headers:
Location:
schema:
type: string
description: URL of the created order.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"409":
$ref: "#/components/responses/Conflict"
/v1/orders/{orderId}:
get:
operationId: getOrder
summary: Get one order
parameters:
- $ref: "#/components/parameters/OrderId"
responses:
"200":
description: Order found.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"404":
$ref: "#/components/responses/NotFound"
patch:
operationId: updateOrder
summary: Update mutable order fields
parameters:
- $ref: "#/components/parameters/OrderId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateOrderRequest"
responses:
"200":
description: Order updated.
content:
application/json:
schema:
$ref: "#/components/schemas/OrderResponse"
"400":
$ref: "#/components/responses/BadRequest"
"404":
$ref: "#/components/responses/NotFound"
components:
parameters:
OrderId:
name: orderId
in: path
required: true
schema:
type: string
StatusParam:
name: status
in: query
schema:
type: string
enum: [draft, paid, canceled]
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
AfterParam:
name: after
in: query
schema:
type: string
description: Cursor returned by the previous response.
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 16
maxLength: 128
schemas:
CreateOrderRequest:
type: object
required: [customerId, items]
properties:
customerId:
type: string
items:
type: array
minItems: 1
items:
type: object
required: [sku, quantity]
properties:
sku:
type: string
quantity:
type: integer
minimum: 1
UpdateOrderRequest:
type: object
minProperties: 1
properties:
status:
type: string
enum: [draft, paid, canceled]
OrderResponse:
type: object
required: [data]
properties:
data:
$ref: "#/components/schemas/Order"
OrderListResponse:
type: object
required: [data, pageInfo]
properties:
data:
type: array
items:
$ref: "#/components/schemas/Order"
pageInfo:
type: object
required: [nextCursor, hasMore]
properties:
nextCursor:
type: [string, "null"]
hasMore:
type: boolean
Order:
type: object
required: [id, status, totalCents, createdAt]
properties:
id:
type: string
status:
type: string
totalCents:
type: integer
createdAt:
type: string
format: date-time
ErrorResponse:
type: object
required: [error]
properties:
error:
type: object
required: [code, message, requestId]
properties:
code:
type: string
message:
type: string
requestId:
type: string
details:
type: array
items:
type: object
responses:
BadRequest:
description: Request validation failed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Conflict:
description: Idempotency key conflicts with a different request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
NotFound:
description: Resource was not found.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
Simpan sebagai openapi.yaml dan minta Claude Code tidak mengusulkan implementasi yang menyimpang dari kontrak ini. Setelah itu, npx @redocly/cli lint openapi.yaml dapat memeriksa sintaks dan referensi. Jika Anda membuat tipe atau client TypeScript dari OpenAPI, tinjau dulu makna tabel endpointnya.
Standarkan Error Dan Status Code
Status code adalah sinyal HTTP di luar. Body JSON adalah detail yang dibaca manusia dan program. Mengembalikan 200 OK dengan { "success": false } membuat monitoring, SDK, dan retry logic menganggap kegagalan sebagai keberhasilan. Tetapkan tabel: input salah adalah 400, belum autentikasi 401, tidak punya izin 403, resource tidak ada 404, duplikasi atau konflik idempotensi 409.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Request body has invalid fields.",
"requestId": "req_01J0RESTAPI001",
"details": [
{
"field": "items[0].quantity",
"reason": "must be greater than or equal to 1"
}
]
}
}
{
"error": {
"code": "IDEMPOTENCY_CONFLICT",
"message": "The same Idempotency-Key was used with a different request body.",
"requestId": "req_01J0RESTAPI002"
}
}
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "Order was not found.",
"requestId": "req_01J0RESTAPI003"
}
}
Untuk pemula: code adalah nama singkat agar program bisa bercabang, message adalah penjelasan untuk log atau UI, dan requestId membantu support mencari log server. details cukup dipakai saat error validasi membutuhkan rincian per field.
Jangan Membiarkan Pagination, Idempotensi, Atau Versi Kabur
API daftar membutuhkan pagination sejak awal. Mengembalikan semua order memang bekerja saat data kecil, tetapi saat tabel membesar, perubahan response menjadi breaking change. Dengan cursor pagination, client mengirim nextCursor dari response sebelumnya sebagai after berikutnya.
{
"data": [
{
"id": "ord_100",
"status": "paid",
"totalCents": 4800,
"createdAt": "2026-06-03T09:00:00Z"
}
],
"pageInfo": {
"nextCursor": "ord_099",
"hasMore": true
}
}
Idempotensi mencegah request berulang membuat efek ganda. POST /orders sangat rentan karena client akan retry setelah gangguan jaringan. Simpan Idempotency-Key dan hash request body untuk waktu terbatas. Key dan body yang sama mengembalikan hasil sebelumnya; key sama dengan body berbeda mengembalikan 409.
Versioning memberi waktu migrasi kepada consumer. Menghapus field wajib, mengubah tipe, mengubah urutan default, atau menambah field wajib di request adalah breaking change dan sebaiknya masuk /v2. Menambah field response opsional biasanya bisa tetap di /v1.
Minta Claude Code Melakukan Review Desain
Sebelum implementasi, gunakan prompt yang konkret.
Review this OpenAPI design.
Check:
- Resource names are nouns
- HTTP methods and status codes match MDN semantics
- 400/401/403/404/409/500 errors share the same JSON shape
- List pagination is unambiguous
- POST creation endpoints that need idempotency keys are identified
- No response change breaks the v1 contract
- Return a table of issues to fix before TypeScript implementation and tests
Dengan ini, Claude Code bertindak sebagai reviewer desain sebelum mengedit file. Mintalah implementasi hanya setelah OpenAPI yang sudah direview disimpan.
Kesalahan Yang Sering Terjadi
Kesalahan pertama adalah endpoint bergaya aksi. Jika POST /createOrder, POST /updateOrderStatus, dan GET /deleteOrder bercampur, URL tidak lagi menunjukkan resource dan operasi dengan jelas. Lebih baik gunakan POST /v1/orders, PATCH /v1/orders/{orderId}, dan DELETE /v1/orders/{orderId}, lalu dokumentasikan pengecualian.
Kesalahan kedua adalah status code yang tidak konsisten. Jika input salah mengembalikan 500, resource tidak ada mengembalikan 200, dan pembuatan sukses kadang 200 kadang 201, client tidak bisa bercabang dengan benar. Standarkan kode sukses juga, bukan hanya error.
Kesalahan ketiga adalah pagination ambigu. Jika page=2 tidak menjelaskan apakah urutannya berdasarkan waktu dibuat atau diperbarui, data baru dapat menyebabkan duplikasi atau baris terlewat. Tulis urutan, batas maksimum limit, dan cara mendeteksi halaman berikutnya.
Kesalahan keempat adalah menyembunyikan validasi hanya di implementasi. Jika quantity >= 1 ada di kode tetapi tidak di OpenAPI, frontend dan SDK hasil generate tidak bisa memakai ulang aturan itu. Batasan harus ada di spesifikasi dan kode.
Checklist Review
- Nama resource konsisten memakai kata benda jamak
- Method sesuai semantik
GET,POST,PATCH, danDELETE - Kode sukses membedakan
200,201, dan204 - Error
400,401,403,404,409, dan500memakai bentuk JSON yang sama - API daftar memiliki
limit,after,nextCursor, danhasMore - Nilai default dan maksimum
limitterdokumentasi - Endpoint create tidak menggandakan data saat retry
- Kriteria breaking change ke
/v2tertulis - Aturan validasi muncul di OpenAPI
- Tabel endpoint dan OpenAPI sudah cocok sebelum tugas diberikan ke Claude Code
Desain REST API bukan soal membuat URL yang terlihat rapi. Ini soal mengubah janji kepada consumer menjadi teks yang jelas dan spesifikasi yang bisa dibaca alat. Claude Code mempercepat implementasi, tetapi janji apa yang harus dijaga sistem tetap harus diputuskan manusia.
Saat dicoba pada API order milik Masa, menyiapkan OpenAPI, tabel endpoint, dan JSON error sebelum meminta Claude Code mengimplementasikan terbukti mengurangi revisi review. Memasukkan Idempotency-Key dan cursor pagination sejak tahap desain juga menghindari tambalan belakangan untuk order ganda dan item daftar yang terlewat.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission safety ladder Claude Code: perluas akses tanpa kehilangan kontrol
Naik dari read-only ke edit terbatas, command bukti, dan cek deploy dengan kontrol yang jelas.
Claude Code Small PR Proof Pack: perubahan kecil yang mudah direview
Paket bukti untuk PR Claude Code: diff, check, URL publik, jalur CTA, dan rollback.
Review gate Claude Code sebelum commit: diff, test, URL publik, dan CTA
Cara memakai Claude Code sebelum commit: diff scope, build, URL publik, link Gumroad, CTA konsultasi, missing test, dan file tidak terkait.