RFC 7807: Problem Details for HTTP APIs
Daftar Isi
Apa Itu RFC 7807?
Mengapa RFC 7807 Penting?
Anatomi Problem Details
Field Wajib vs Opsional
Media Type yang Digunakan
Ekstensi Custom Fields
Implementasi Golang β Production Ready
Struktur Project
Core Problem Details Package
Error Registry & Problem Types
Middleware untuk HTTP Handler
Contoh Handler Lengkap
Validasi & Error Extension
Logging & Observability
Testing Problem Details
Perbandingan: Sebelum vs Sesudah RFC 7807
Best Practices & Anti-Pattern
Penutup
Apa Itu RFC 7807?
RFC 7807 adalah standar IETF (Internet Engineering Task Force) yang berjudul "Problem Details for HTTP APIs". Standar ini dipublikasikan pertama kali pada Maret 2016 dan mendefinisikan sebuah format respons error yang konsisten untuk HTTP API. Pada September 2022, versi kedua yaitu RFC 9457 dirilis sebagai pembaruan minor, namun RFC 7807 tetap menjadi rujukan yang paling banyak digunakan dan dikenal di industri.
Secara sederhana, RFC 7807 menjawab satu pertanyaan mendasar yang sering diabaikan oleh banyak developer: "Ketika API-ku error, bagaimana aku harus memberi tahu client secara konsisten?"
Sebelum adanya standar ini, setiap API punya format error-nya sendiri. Ada yang pakai {"error": "..."}, ada yang pakai {"message": "..."}, ada yang pakai {"code": 400, "description": "..."}, dan seterusnya. Tidak ada kesepakatan. Ini menyulitkan client β terutama ketika satu aplikasi mengonsumsi banyak API dari vendor berbeda.
RFC 7807 hadir sebagai lingua franca untuk error response.
Mengapa RFC 7807 Penting?
Bayangkan Anda membangun sebuah sistem e-commerce yang mengintegrasikan tiga layanan berbeda: payment gateway, inventory service, dan shipping service. Ketika terjadi error, masing-masing layanan mungkin mengembalikan format yang berbeda. Tim frontend harus menulis parser khusus untuk setiap layanan. Tim backend harus mendokumentasikan format error yang berbeda untuk tiap service. Ini adalah pemborosan waktu yang serius.
Dengan RFC 7807, semua service mematuhi satu kontrak. Manfaatnya antara lain:
Konsistensi β Semua error dari semua service punya struktur yang sama, sehingga client hanya perlu satu cara untuk memproses error.
Self-descriptive β Error mengandung URI yang bisa dibuka untuk mendapat penjelasan lebih lanjut. Ini mengurangi beban dokumentasi.
Extensible β Format ini mendukung field tambahan, sehingga bisa dikustomisasi tanpa melanggar kontrak.
Machine-readable & Human-readable β Formatnya JSON, mudah diproses oleh kode, namun juga mudah dibaca oleh manusia saat debugging.
Anatomi Problem Details
RFC 7807 mendefinisikan objek JSON dengan beberapa field yang telah ditentukan maknanya. Berikut contoh lengkap sebuah Problem Details response:
Perhatikan bahwa field errors di atas adalah ekstensi custom β bukan bagian dari standar RFC 7807 itu sendiri, namun diperbolehkan dan sangat umum digunakan.
Field Wajib vs Opsional
RFC 7807 mendefinisikan lima field standar. Tidak ada satupun yang benar-benar wajib, namun praktik terbaik mengharuskan setidaknya beberapa di antaranya hadir.
type (string, URI) adalah identifier unik untuk jenis masalah ini. Nilainya harus berupa URI absolut yang, idealnya, ketika dibuka, memberikan dokumentasi tentang masalah tersebut. Jika tidak ada URI khusus, gunakan "about:blank" yang berarti menggunakan status HTTP code sebagai satu-satunya informasi. Contoh: "https://api.yourapp.com/problems/insufficient-funds".
title (string) adalah ringkasan singkat yang bisa dibaca manusia tentang jenis masalah. Penting: nilai ini tidak boleh berubah antar request untuk masalah yang sama. Ia mendeskripsikan jenis masalah, bukan instansi spesifiknya. Contoh: "Insufficient Funds".
status (integer) adalah HTTP status code yang relevan. Meskipun sudah ada di HTTP header, menyertakannya di body berguna ketika response body dilogging atau diproses secara terpisah dari header. Contoh: 422.
detail (string) adalah penjelasan spesifik tentang instansi masalah ini yang berguna untuk client. Berbeda dengan title, field ini boleh (dan sebaiknya) bervariasi per request. Gunakan field ini untuk memberikan konteks yang berguna bagi pengguna atau developer. Contoh: "Your balance of $0.50 is less than the required amount of $10.00".
instance (string, URI) adalah URI yang mengidentifikasi instansi spesifik dari masalah ini. Bisa berupa URI absolut atau relatif. Berguna untuk logging dan tracing β client bisa menyertakannya ketika melaporkan bug. Contoh: "/transactions/abc-123".
Media Type yang Digunakan
RFC 7807 mendefinisikan dua media type baru:
application/problem+jsonβ untuk representasi JSONapplication/problem+xmlβ untuk representasi XML
Dalam praktik di industri, hampir semua orang menggunakan JSON. Yang penting, response harus menggunakan Content-Type: application/problem+json, bukan application/json. Perbedaan ini sinyal penting bagi client bahwa response ini adalah error dalam format RFC 7807, bukan respons sukses yang berbentuk JSON biasa.
Ekstensi Custom Fields
Salah satu kekuatan RFC 7807 adalah kemampuannya untuk di-extend. Anda bebas menambahkan field apapun ke dalam objek Problem Details, asalkan tidak bertabrakan dengan lima field standar di atas. Beberapa ekstensi yang umum digunakan:
Implementasi Golang β Production Ready
Sekarang kita masuk ke bagian yang paling menarik. Kita akan membangun implementasi RFC 7807 yang lengkap, production-ready, dan mengikuti idiomatic Go.
Struktur Project
Core Problem Details Package
File internal/problems/problem.go adalah jantung dari implementasi kita. Di sinilah kita mendefinisikan struct dan semua helper yang diperlukan.
Error Registry & Problem Types
Membuat problem types tersebar di mana-mana adalah anti-pattern. Sebaiknya, semua problem types diregistrasi di satu tempat. File internal/problems/registry.go:
Middleware untuk HTTP Handler
Middleware ini sangat penting untuk production. Ia menangkap panic, memastikan semua error terdokumentasi, dan menambahkan trace ID ke setiap response.
Validasi & Error Extension
Salah satu use case terpenting RFC 7807 adalah validation error. Kita perlu menampilkan field mana yang bermasalah dan mengapa. Berikut implementasinya menggunakan library go-playground/validator.
Contoh Handler Lengkap
Sekarang mari kita lihat bagaimana semua komponen di atas digunakan bersama dalam handler yang nyata.
Logging & Observability
Pada implementasi WriteError di middleware, kita sudah menyertakan structured logging menggunakan log/slog (standar library Go 1.21+). Setiap error di-log dengan informasi yang cukup untuk debugging:
Testing Problem Details
Testing adalah bagian yang tidak terpisahkan dari implementasi production-ready. Berikut contoh testing yang komprehensif.
Contoh Response Lengkap
Berikut adalah contoh response yang akan dihasilkan oleh implementasi kita untuk berbagai skenario:
Validation Error (422):
Conflict Error (409):
Insufficient Funds (422):
Perbandingan: Sebelum vs Sesudah RFC 7807
Untuk membuat perbedaannya semakin nyata, berikut perbandingan kode sebelum dan sesudah mengadopsi RFC 7807.
Sebelum (tidak konsisten, hard to maintain):
Sesudah (konsisten, aman, standar):
Best Practices & Anti-Pattern
Setelah membahas implementasi, penting untuk mengetahui apa yang harus dilakukan dan apa yang harus dihindari.
Yang harus dilakukan: Selalu gunakan application/problem+json sebagai Content-Type, bukan application/json, untuk response error. Ini sinyal penting bagi client.
Yang harus dilakukan: Daftarkan semua problem types di satu file registry. Ini memudahkan dokumentasi, auditing, dan memastikan konsistensi type URI dan title.
Yang harus dilakukan: Sertakan trace_id di setiap error response. Ini adalah investasi kecil yang menghemat banyak waktu saat debugging di production.
Yang harus dilakukan: Bedakan antara title (deskripsi jenis masalah, tidak berubah) dan detail (deskripsi instansi spesifik, boleh bervariasi).
Yang TIDAK boleh dilakukan: Jangan pernah mengekspos stack trace, nama database, nama internal library, atau pesan error sistem ke dalam field detail untuk error 5xx. Log detail tersebut di server, kirim pesan umum ke client.
Yang TIDAK boleh dilakukan: Jangan membuat problem types yang terlalu granular sampai satu handler bisa menghasilkan puluhan types berbeda. Sebaliknya, jangan juga terlalu generik sehingga semua error masuk ke satu tipe "general-error". Temukan keseimbangan yang baik.
Yang TIDAK boleh dilakukan: Jangan mengembalikan status 200 OK untuk error, lalu menyembunyikan status error di dalam body. Ini melanggar semantik HTTP dan membingungkan proxy, monitoring tool, dan client.
Yang TIDAK boleh dilakukan: Jangan biarkan field type menunjuk ke URI yang tidak ada atau yang mengembalikan 404. Jika URI belum siap, gunakan "about:blank" sementara.
Penutup
RFC 7807 adalah salah satu standar API yang paling elegan: sederhana, extensible, dan pragmatis. Ia tidak memaksakan Anda melakukan perombakan besar-besaran, namun secara bertahap membawa konsistensi yang sangat berharga ke seluruh ekosistem API Anda.
Implementasi yang kita bangun di artikel ini mengikuti beberapa prinsip utama: fail safely (panic di-recover, internal errors tidak bocor), observable (trace ID dan structured logging di setiap error), consistent (semua error melewati satu jalur WriteError), dan extensible (mudah menambahkan domain-specific problem types baru).
Jika Anda membangun API baru hari ini, ada satu alasan yang sangat kuat untuk mengadopsi RFC 7807 sejak awal: jauh lebih mudah membangun dengan standar dari awal daripada melakukan migrasi di tengah jalan ketika sudah ada puluhan client yang bergantung pada format error lama Anda.
Selamat membangun API yang lebih baik! π
Referensi:
Last updated