Tips & Tricks (Diperbarui: 3/6/2026)

Desain REST API dengan Claude Code: endpoint, error, pagination, idempotensi

Rancang REST API dengan Claude Code sebelum coding: OpenAPI, error, pagination, idempotensi, dan checklist.

Desain REST API dengan Claude Code: endpoint, error, pagination, idempotensi

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.

IstilahPenjelasan sederhanaContoh
ResourceKata benda yang dikelola APIorders, customers
EndpointGabungan URL dan metode HTTPGET /v1/orders
MethodVerb HTTP yang menunjukkan operasiGET, POST, PATCH
Status codeAngka untuk memberi sinyal sukses, input salah, belum login, dan lainnya200, 201, 400, 404
ValidationPemeriksaan bahwa request berbentuk benarFormat email, jumlah minimum
IdempotensiOperasi yang sama dapat diulang tanpa mengubah state akhirSatu 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.

TujuanMethod dan pathSuksesAturan penting
Daftar orderGET /v1/orders?status=paid&limit=20&after=ord_123200 OKCursor pagination dengan sort stabil
Buat orderPOST /v1/orders201 CreatedTerima Idempotency-Key dan kembalikan Location
Detail orderGET /v1/orders/{orderId}200 OKJika tidak ada, kembalikan 404
Update orderPATCH /v1/orders/{orderId}200 OKUpdate sebagian; patch kosong adalah 400
Batalkan orderPOST /v1/orders/{orderId}/cancel200 OKPerlakukan 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, dan DELETE
  • Kode sukses membedakan 200, 201, dan 204
  • Error 400, 401, 403, 404, 409, dan 500 memakai bentuk JSON yang sama
  • API daftar memiliki limit, after, nextCursor, dan hasMore
  • Nilai default dan maksimum limit terdokumentasi
  • Endpoint create tidak menggandakan data saat retry
  • Kriteria breaking change ke /v2 tertulis
  • 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.

#claude-code #rest-api #openapi #typescript #backend
Gratis

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.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.