DEV Community

Cover image for Penanganan Error REST API Praktik Terbaik: Kode Status, RFC 9457, dan Error yang Bisa Dicoba Ulang
Walse
Walse

Posted on Originally published at apidog.com

Penanganan Error REST API Praktik Terbaik: Kode Status, RFC 9457, dan Error yang Bisa Dicoba Ulang

Praktik Terbaik Penanganan Kesalahan API REST

Respons kesalahan API adalah bagian dari kontraknya. Klien menguraikannya, logika percobaan ulang bercabang berdasarkan respons tersebut, dan tim dukungan menggunakannya saat mendiagnosis masalah. Namun banyak tim hanya merancang alur sukses secara rinci, lalu membiarkan kesalahan mengikuti perilaku default framework. Hasilnya: beberapa format kesalahan dalam satu API, respons 200 dengan "success": false, atau jejak tumpukan yang membocorkan skema basis data.

Coba Apidog hari ini

Panduan ini membahas cara memilih kode status HTTP, membakukan badan kesalahan dengan RFC 9457 Problem Details, memisahkan kode mesin dari pesan manusia, menandai kesalahan yang dapat dicoba ulang, menjaga kerahasiaan respons, serta menguji setiap jalur kegagalan menggunakan Apidog.

Mulai dari kode status, bukan badan respons

HTTP sudah menyediakan lapisan pertama semantik kesalahan. RFC 9110 membagi kode status menjadi:

  • 4xx: kesalahan berada pada permintaan klien; mengulang permintaan yang sama biasanya akan gagal.
  • 5xx: server atau dependensinya gagal; permintaan klien mungkin valid.

Klien umum, proksi, cache, dan pustaka percobaan ulang sering membuat keputusan berdasarkan kode status tanpa membaca JSON. Karena itu, pilih status dengan konsisten. Gunakan referensi kode status HTTP MDN saat merancang API.

Situasi Gunakan Bukan Alasan
JSON rusak, tipe konten salah, atau bidang wajib hilang 400 Bad Request 422 Server tidak dapat mengurai atau memahami permintaan
Sintaks valid, tetapi nilai melanggar aturan domain 422 Unprocessable Content 400 Permintaan dapat diurai, tetapi nilainya tidak valid
Kredensial tidak ada atau token kedaluwarsa 401 Unauthorized 403 Klien belum membuktikan identitas; kirim WWW-Authenticate
Identitas valid, tetapi tidak memiliki izin 403 Forbidden 401 Otentikasi ulang tidak akan membantu
Sumber daya tidak ada atau keberadaannya tidak boleh dikonfirmasi 404 Not Found 410 Default yang aman dan mencegah pemeriksaan tidak sah
Sumber daya sengaja dihapus secara permanen 410 Gone 404 Klien dan perayap dapat menghapus referensinya
Kunci duplikat, versi lama, atau konflik pengeditan 409 Conflict 400 Permintaan valid, tetapi bertentangan dengan status saat ini
Klien melampaui batas kecepatan 429 Too Many Requests 503 Sertakan Retry-After
Pengecualian tidak tertangani di kode Anda 500 Internal Server Error 502 Server Anda mengalami kegagalan
Layanan upstream mengembalikan data tidak valid ke gateway 502 Bad Gateway 500 Kegagalan berada di sisi upstream
Server kelebihan beban atau sedang dipelihara 503 Service Unavailable 500 Kesalahan ini bersifat sementara
Layanan upstream mengalami timeout 504 Gateway Timeout 500 Membedakan dependensi lambat dari kode yang rusak

Dua hal sering tertukar:

  1. 401 vs 403 adalah keputusan keamanan. Mengembalikan 403 kepada pemanggil yang belum terautentikasi dapat membocorkan bahwa sumber daya tersebut ada.
  2. 429 harus disertai Retry-After. Tanpa petunjuk penundaan, klien dapat mencoba ulang dalam perulangan ketat. Lihat panduan pembatasan kecepatan API untuk algoritme dan perhitungan header.

Gunakan satu format: RFC 9457 Problem Details

Setelah kode status ditentukan, semua kesalahan API sebaiknya menggunakan satu tipe media dan satu skema. Standarnya adalah RFC 9457 Problem Details, yang disajikan sebagai:

Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode

RFC 9457 mendefinisikan lima anggota inti:

  • type: URI yang mengidentifikasi kategori kesalahan
  • title: ringkasan singkat yang mudah dibaca
  • status: kode HTTP
  • detail: penjelasan kesalahan pada kejadian tersebut
  • instance: URI untuk kegagalan spesifik

Anggota lain dapat ditambahkan sebagai ekstensi. Penjelasan RFC 9457 membahas spesifikasi dan perbedaannya dari RFC 7807.

Contoh kegagalan validasi pada endpoint pembayaran:

POST /v1/payments HTTP/1.1
Content-Type: application/json

{ "amount": -1400, "currency": "USD", "source": "card_8xKt2" }
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validasi permintaan gagal",
  "status": 422,
  "detail": "Satu atau lebih bidang gagal validasi.",
  "instance": "/v1/payments/requests/req_9f3c1a7b",
  "code": "PAYMENT_VALIDATION_FAILED",
  "errors": [
    {
      "field": "amount",
      "code": "AMOUNT_NOT_POSITIVE",
      "message": "jumlah harus berupa bilangan bulat positif dalam satuan kecil"
    }
  ],
  "request_id": "req_9f3c1a7b"
}
Enter fullscreen mode Exit fullscreen mode

errors[] adalah anggota ekstensi dan sangat berguna bagi klien. Frontend dapat memetakan setiap kesalahan ke bidang formulir yang tepat, bukan menampilkan satu pesan umum.

Pilih format jalur bidang yang stabil—misalnya JSON Pointer atau jalur bertitik—dan gunakan format tersebut di semua endpoint.

Terapkan format yang sama untuk kesalahan yang dihasilkan framework, proxy, dan gateway. Jika handler Anda mengembalikan Problem Details, tetapi load balancer mengembalikan HTML untuk 502, klien tetap harus memelihara dua parser.

Pisahkan kode mesin dan pesan manusia

Contoh di atas memiliki code dan message karena keduanya melayani audiens berbeda.

Kode yang dapat dibaca mesin

Kode seperti berikut adalah bagian dari kontrak:

AMOUNT_NOT_POSITIVE
CURRENCY_UNSUPPORTED
IDEMPOTENCY_KEY_REUSED
Enter fullscreen mode Exit fullscreen mode

Kode harus stabil, terdokumentasi, dan dapat digunakan untuk percabangan program. Jangan meminta klien mengurai prosa:

if (message.includes("positive")) {
  // rapuh: perubahan teks dapat mematahkan perilaku klien
}
Enter fullscreen mode Exit fullscreen mode

Pesan untuk manusia

Pesan ditujukan bagi pengembang yang membaca log atau respons. Pesan boleh diperbaiki dan dilokalkan tanpa mengubah perilaku klien.

Gunakan pesan yang menjelaskan masalah dan cara memperbaikinya:

jumlah harus berupa bilangan bulat positif dalam satuan kecil
Enter fullscreen mode Exit fullscreen mode

lebih berguna daripada:

jumlah tidak valid
Enter fullscreen mode Exit fullscreen mode

Jika API mendukung beberapa bahasa, lokalkan message, tetapi pertahankan code. Pemisahan ini juga penting bagi konsumen berbasis LLM. Kesalahan terstruktur membantu agen AI menentukan langkah pemulihan; lihat desain kesalahan API untuk agen AI.

Jangan bocorkan informasi sensitif

Respons kesalahan adalah saluran pengintaian yang menarik bagi penyerang. Middleware kesalahan harus memastikan hal-hal berikut tidak pernah dikirim kepada klien:

  • Jejak tumpukan, nama kelas, atau jalur file
  • SQL mentah, fragmen kueri, atau kesalahan ORM
  • Nama host internal, alamat IP, port, atau nama layanan
  • Versi pustaka dan banner framework
  • Rahasia, token, atau string koneksi
  • Keberadaan akun pengguna pada alur login dan reset kata sandi

Gunakan pola berikut:

  1. Tangkap semua pengecualian di batas aplikasi.
  2. Catat pengecualian lengkap di server.
  3. Sertakan ID permintaan dalam log.
  4. Kembalikan Problem Details generik kepada ```

Log server menyimpan detail sebenarnya, sedangkan tim dukungan dapat menghubungkan respons dengan log melalui request_id.

Tandai kesalahan yang dapat dicoba ulang

Setiap respons kesalahan harus menjawab pertanyaan: haruskah klien mencoba lagi?

Secara umum:

  • 429, 502, 503, dan 504: dapat dicoba ulang dengan exponential backoff dan jitter.
  • 500: ambigu, tetapi biasanya layak dicoba ulang satu kali secara hati-hati.
  • Sebagian besar 4xx lain: terminal; jangan ulangi permintaan yang sama.

Timeout perlu perhatian khusus. Permintaan mungkin berhasil setelah klien berhenti menunggu. Endpoint yang mengubah data harus mendukung kunci idempoten agar percobaan ulang tidak menagih atau membuat data dua kali.

Anda juga dapat membuat keputusan ini eksplisit melalui anggota ekstensi:


json
{
  "type": "https://api.example.com/problems/rate-limited",
  "title": "Terlalu banyak permintaan",
  "status": 429,
  "code": "RATE_LIMITED",
  "retryable": true,
  "retry_after_seconds": 30
}


Enter fullscreen mode Exit fullscreen mode

retryable memungkinkan server mengganti default status. Misalnya, subkode tertentu pada 500 dapat ditandai terminal jika percobaan ulang berisiko merusak status.

Dokumentasikan perilaku ini sekali agar semua SDK klien menerapkan strategi percobaan ulang yang seragam.

Kelola korelasi dan versi kontrak

Selalu gunakan ID permintaan

Terima header X-Request-Id dari klien atau buat ID baru. Tulis ID tersebut di setiap baris log dan kembalikan sebagai request_id pada setiap respons kesalahan.

Dalam sistem terdistribusi, sebarkan juga traceparent W3C agar permintaan dapat dilacak di seluruh layanan.

Perlakukan kontrak kesalahan seperti API

Penambahan anggota ekstensi atau kode kesalahan baru biasanya aman. Perubahan berikut adalah breaking change:

  • Mengganti nama errors[].field
  • Mengubah arti kode yang sudah ada
  • Mengubah format Problem Details menjadi format ad hoc
  • Menghapus anggota yang sebelumnya digunakan klien

Pertahankan URI type lama untuk semantik lama. Gunakan URI baru untuk semantik baru, dan dokumentasikan bahwa anggota atau kode yang tidak dikenal harus diabaikan.

Aturan kompatibilitas ke depan ini memungkinkan kontrak berkembang tanpa harus membuat versi API baru.

Uji setiap jalur kesalahan di Apidog

Kontrak kesalahan cepat membusuk jika tidak diuji. Alur sukses biasanya diuji dalam demo, sedangkan cabang 422 atau 503 baru terlihat saat pelanggan menemukannya.

Gunakan Apidog untuk menjadikan kegagalan sebagai bagian dari pengujian.

1. Buat skenario pengujian server

Untuk setiap endpoint, tambahkan kasus seperti:

  • Kredensial hilang → 401
  • Peran tidak memadai → 403
  • Jumlah negatif → 422 dengan errors[0].code bernilai AMOUNT_NOT_POSITIVE
  • Lalu lintas berlebih → 429 dengan header Retry-After

Gunakan asersi untuk memeriksa:

  • Kode status
  • Header
  • Tipe media
  • Struktur badan
  • Kesesuaian dengan JSON Schema Problem Details

Dengan begitu, perubahan kontrak menyebabkan kegagalan CI, bukan insiden produksi. Lihat Panduan asersi API untuk contoh pola asersi.

2. Gunakan server tiruan untuk klien

Frontend dan SDK perlu menguji respons 4xx dan 5xx sebelum backend dapat menghasilkan kondisi tersebut secara konsisten.

Server tiruan Apidog dapat mengembalikan badan Problem Details dari spesifikasi API, sehingga Anda dapat mensimulasikan:

  • 503 dengan Retry-After: 120
  • 409 saat pengiriman ganda
  • Payload validasi lengkap dengan errors[]

Kemudian amati bagaimana klien menampilkan pesan, memperbaiki input, dan menjalankan percobaan ulang—tanpa stub Express manual atau perubahan sementara pada kode backend.

Rancang kontrak kesalahan, implementasikan sebagai skenario dan mock, lalu jalankan semuanya di CI. Anda dapat mengunduh Apidog dan mengimpor spesifikasi OpenAPI untuk mulai membuat mock dalam beberapa menit.

FAQ

Haruskah menggunakan 400 atau 422 untuk validasi?

Gunakan 400 ketika permintaan salah format dan server tidak dapat memahaminya, seperti JSON tidak valid, tipe konten salah, atau bidang wajib hilang.

Gunakan 422 ketika permintaan berhasil diurai, tetapi nilainya melanggar aturan domain, seperti jumlah pembayaran negatif atau mata uang yang tidak didukung.

Yang terpenting adalah konsistensi di semua endpoint.

Apa itu application/problem+json?

application/problem+json adalah tipe media RFC 9457 untuk Problem Details, yaitu format kesalahan JSON standar bagi API HTTP.

Respons ini membawa type, title, status, detail, dan instance, serta ekstensi seperti errors[]. Tipe media yang terdaftar memungkinkan klien umum dan middleware mengenali kesalahan tanpa konfigurasi khusus.

Kesalahan HTTP mana yang boleh dicoba ulang otomatis?

Coba ulang 429, 502, 503, dan 504 dengan exponential backoff dan jitter. Hormati Retry-After jika tersedia.

Anggap 500 layak untuk satu percobaan ulang yang hati-hati. Jangan mencoba ulang respons 4xx lainnya dengan permintaan identik.

Untuk endpoint yang mengubah data, gunakan kunci idempoten agar percobaan ulang tidak menghasilkan tagihan atau data ganda.

Bagaimana menguji respons kesalahan tanpa merusak backend?

Gunakan simulasi. Arahkan klien ke server tiruan Apidog yang mengembalikan badan 4xx dan 5xx sesuai spesifikasi API, lalu verifikasi perilaku rendering dan percobaan ulang.

Di sisi server, buat skenario untuk payload tidak valid, autentikasi yang hilang, konflik, dan lalu lintas berlebih. Lakukan asersi pada kode status, header, serta skema badan kesalahan, lalu jalankan kedua rangkaian pengujian di CI.

Top comments (0)