Best Practices Struktur JSON untuk REST API
Desain response JSON yang konsisten adalah salah satu hal yang paling underrated dalam membangun REST API, tapi dampaknya besar untuk developer experience — baik untuk tim internal maupun konsumen eksternal API kamu. API dengan struktur JSON yang berantakan dan tidak konsisten antar endpoint bikin frontend developer harus terus-menerus cek dokumentasi atau bahkan menebak-nebak. Artikel ini merangkum praktik terbaik yang sudah teruji dipakai banyak API populer (Stripe, GitHub, Twitter/X) untuk menyusun struktur JSON REST API.
1. Naming Convention: Konsisten camelCase atau snake_case
Pilih satu gaya penamaan key dan pakai konsisten di seluruh API — jangan campur. Dua pilihan paling umum:
// camelCase (umum di ekosistem JavaScript)
{ "firstName": "Budi", "createdAt": "2026-07-01" }
// snake_case (umum di ekosistem Python/Ruby)
{ "first_name": "Budi", "created_at": "2026-07-01" }
Tidak ada yang secara objektif "lebih benar" — yang penting konsisten. Kalau tim frontend kamu mayoritas JavaScript, camelCase biasanya lebih natural karena langsung cocok dengan konvensi penamaan variabel JS.
Konsistensi ini juga sebaiknya diterapkan di seluruh siklus hidup API, bukan cuma di awal peluncuran. Kalau tim kamu berkembang dan endpoint baru terus ditambahkan oleh developer berbeda, ada baiknya menuliskan aturan ini secara eksplisit di style guide internal, bukan cuma mengandalkan konvensi tidak tertulis yang mudah terlewat.
2. Gunakan Envelope Secukupnya, Jangan Berlebihan
Envelope adalah pembungkus data utama di dalam struktur tambahan, misalnya:
{
"data": {
"id": 42,
"name": "Produk A"
},
"meta": {
"requestId": "abc-123"
}
}
Envelope berguna untuk menyisipkan metadata tanpa mengganggu data utama. Tapi jangan berlebihan membungkus — response sederhana seperti health check endpoint tidak perlu envelope kompleks. Aturan praktis: pakai envelope kalau kamu memang butuh menyertakan metadata (pagination, request id, warning); kalau tidak, langsung saja return data-nya.
3. Format Response untuk Single Resource vs Collection
Bedakan jelas antara response untuk satu resource dan response untuk daftar (collection):
// GET /users/42 — single resource
{
"id": 42,
"name": "Sarah Amelia",
"email": "sarah@example.com"
}
// GET /users — collection
{
"data": [
{ "id": 42, "name": "Sarah Amelia" },
{ "id": 43, "name": "Budi Santoso" }
],
"pagination": {
"page": 1,
"perPage": 20,
"totalItems": 145
}
}
Collection biasanya butuh info pagination, sementara single resource tidak. Konsistensi ini membuat frontend bisa membuat helper function generik untuk parsing response tanpa harus cek tipe endpoint dulu.
4. Format Error yang Konsisten dan Informatif
Ini salah satu bagian paling sering diabaikan. Error response yang baik harus memberi cukup informasi untuk debugging tanpa expose detail internal sistem yang sensitif:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Data yang dikirim tidak valid",
"details": [
{ "field": "email", "issue": "Format email tidak valid" },
{ "field": "age", "issue": "Harus berupa angka positif" }
]
}
}
Sertakan code yang bisa dipakai program untuk logic (bukan cuma pesan human-readable), plus details per field kalau errornya validasi. Hindari mengembalikan stack trace mentah atau pesan error database langsung ke client — selain berantakan, itu risiko keamanan karena bisa membocorkan struktur internal sistem.
5. Gunakan HTTP Status Code dengan Benar
JSON body saja tidak cukup — pasangkan dengan HTTP status code yang tepat: 200 untuk sukses, 201 untuk resource baru dibuat, 400 untuk request tidak valid, 401/403 untuk masalah autentikasi/otorisasi, 404 untuk resource tidak ditemukan, 500 untuk error server. Jangan selalu return 200 lalu menaruh status error di body JSON — ini anti-pattern yang bikin banyak HTTP client library dan monitoring tool jadi tidak berguna.
6. Format Tanggal yang Konsisten: ISO 8601
Selalu pakai format ISO 8601 untuk tanggal dan waktu, misalnya "2026-07-15T09:30:00Z". Format ini universal, bisa langsung di-parse oleh hampir semua bahasa pemrograman, dan menghindari ambiguitas format tanggal regional (misalnya DD/MM/YYYY vs MM/DD/YYYY yang sering bikin bug).
7. Null vs Field yang Tidak Ada
Tentukan aturan jelas: apakah field yang kosong dikirim sebagai null, atau dihilangkan sepenuhnya dari response? Konsistensi ini penting supaya client tidak perlu handle dua skenario berbeda untuk arti yang sama.
// pilihan 1: selalu sertakan field, null kalau kosong
{ "middleName": null }
// pilihan 2: hilangkan field kalau kosong
{ }
Pilihan 1 lebih predictable untuk client (mereka selalu tahu field apa saja yang mungkin ada), tapi payload sedikit lebih besar. Pilihan 2 lebih ringkas tapi client harus selalu cek field !== undefined sebelum mengaksesnya.
8. Versioning API
Sertakan versi API supaya perubahan struktur di masa depan tidak langsung merusak client lama. Bisa lewat URL (/v1/users), header (Accept: application/vnd.api+json;version=1), atau query parameter. URL versioning paling sederhana dan paling umum dipakai karena mudah dipahami dan di-cache.
9. Jangan Nested Terlalu Dalam
Struktur JSON yang bersarang lebih dari 3-4 level biasanya tanda desain data model yang perlu dievaluasi ulang, atau kandidat kuat untuk dipecah jadi endpoint terpisah. Nested terlalu dalam bikin JSONPath query dan parsing di client jadi rumit dan rawan error.
10. Dokumentasikan dengan OpenAPI/Swagger
Sebagus apapun struktur JSON kamu, tanpa dokumentasi yang jelas tetap menyulitkan konsumen API. Gunakan spesifikasi OpenAPI (dulu Swagger) untuk mendokumentasikan schema request/response secara formal, sehingga bisa auto-generate dokumentasi interaktif dan bahkan client SDK.
11. Konsisten Soal Tipe Data
Field yang sama harus selalu punya tipe data yang sama di semua response, jangan sampai kadang berupa string kadang number. Contoh kasus yang sering terjadi: field harga kadang dikirim sebagai "price": "50000" (string) di satu endpoint dan "price": 50000 (number) di endpoint lain — ini bikin client harus selalu defensive-check tipe data sebelum diproses. Untuk angka yang melibatkan uang, pertimbangkan juga menyimpan dalam satuan terkecil (misalnya sen/rupiah utuh sebagai integer) untuk menghindari masalah floating-point precision saat kalkulasi.
12. Sediakan Field Identifikasi yang Stabil
Selain id numerik internal, banyak API modern juga menyediakan field seperti uuid atau slug yang lebih aman diekspos ke publik dan tidak gampang ditebak urutannya (menghindari enumeration attack di mana orang bisa menebak ID resource lain dengan increment angka). Pertimbangkan juga menyertakan createdAt dan updatedAt secara default di setiap resource, karena hampir selalu dibutuhkan client untuk keperluan cache invalidation atau sorting.
Best Practices for Structuring JSON in REST APIs
Designing a consistent JSON response is one of the most underrated aspects of building a REST API, yet it has a huge impact on developer experience — for internal teams and external API consumers alike. An API with a messy, inconsistent JSON structure across endpoints forces frontend developers to constantly re-check documentation or even guess. This article rounds up battle-tested best practices used by many popular APIs (Stripe, GitHub, Twitter/X) for structuring JSON in a REST API.
1. Naming Convention: Consistent camelCase or snake_case
Pick one key naming style and use it consistently across the entire API — don't mix them. The two most common options:
// camelCase (common in the JavaScript ecosystem)
{ "firstName": "Budi", "createdAt": "2026-07-01" }
// snake_case (common in the Python/Ruby ecosystem)
{ "first_name": "Budi", "created_at": "2026-07-01" }
Neither is objectively "more correct" — what matters is consistency. If your frontend team is mostly JavaScript, camelCase usually feels more natural since it matches JS variable naming conventions directly.
2. Use an Envelope Sparingly, Not Excessively
An envelope wraps the main data inside an additional structure, for example:
{
"data": {
"id": 42,
"name": "Product A"
},
"meta": {
"requestId": "abc-123"
}
}
An envelope is useful for attaching metadata without cluttering the main data. But don't over-wrap things — a simple response like a health check endpoint doesn't need a complex envelope. Rule of thumb: use an envelope when you actually need to include metadata (pagination, request id, warnings); otherwise, just return the data directly.
3. Format Responses Differently for Single Resources vs Collections
Clearly distinguish between a response for a single resource and a response for a list (collection):
// GET /users/42 — single resource
{
"id": 42,
"name": "Sarah Amelia",
"email": "sarah@example.com"
}
// GET /users — collection
{
"data": [
{ "id": 42, "name": "Sarah Amelia" },
{ "id": 43, "name": "Budi Santoso" }
],
"pagination": {
"page": 1,
"perPage": 20,
"totalItems": 145
}
}
Collections typically need pagination info, while single resources don't. This consistency lets the frontend build a generic helper function for parsing responses without first checking the endpoint type.
4. Consistent, Informative Error Format
This is one of the most commonly neglected parts. A good error response should give enough information for debugging without exposing sensitive internal system details:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The submitted data is invalid",
"details": [
{ "field": "email", "issue": "Invalid email format" },
{ "field": "age", "issue": "Must be a positive number" }
]
}
}
Include a code that programs can use for logic (not just a human-readable message), plus per-field details for validation errors. Avoid returning raw stack traces or raw database error messages straight to the client — besides being messy, it's a security risk since it can leak internal system structure.
5. Use HTTP Status Codes Correctly
The JSON body alone isn't enough — pair it with the right HTTP status code: 200 for success, 201 for a newly created resource, 400 for an invalid request, 401/403 for auth/authorization issues, 404 for a missing resource, 500 for a server error. Don't always return 200 and put an error status inside the JSON body instead — that's an anti-pattern that renders many HTTP client libraries and monitoring tools useless.
6. Consistent Date Format: ISO 8601
Always use the ISO 8601 format for dates and times, e.g. "2026-07-15T09:30:00Z". It's universal, can be parsed directly by nearly every programming language, and avoids ambiguous regional date formats (like DD/MM/YYYY vs MM/DD/YYYY, which often causes bugs).
7. Null vs a Missing Field
Set a clear rule: is an empty field sent as null, or omitted entirely from the response? This consistency matters so clients don't have to handle two different scenarios for the same meaning.
// option 1: always include the field, null if empty
{ "middleName": null }
// option 2: omit the field if empty
{ }
Option 1 is more predictable for clients (they always know which fields might exist), but the payload is slightly larger. Option 2 is leaner but clients must always check field !== undefined before accessing it.
8. API Versioning
Include an API version so future structural changes don't immediately break old clients. This can go through the URL (/v1/users), a header (Accept: application/vnd.api+json;version=1), or a query parameter. URL versioning is the simplest and most commonly used since it's easy to understand and cache.
9. Don't Nest Too Deeply
A JSON structure nested more than 3-4 levels deep is usually a sign the data model needs re-evaluating, or a strong candidate for splitting into a separate endpoint. Excessive nesting makes JSONPath queries and client-side parsing complicated and error-prone.
10. Document With OpenAPI/Swagger
No matter how good your JSON structure is, it's still hard for API consumers without clear documentation. Use the OpenAPI spec (formerly Swagger) to formally document your request/response schemas, enabling auto-generated interactive docs and even client SDKs.
11. Be Consistent About Data Types
The same field should always have the same data type across every response — don't let it be a string sometimes and a number other times. A common real-world case: a price field sometimes sent as "price": "50000" (string) on one endpoint and "price": 50000 (number) on another — forcing clients to always defensively check the type before processing it. For monetary values, also consider storing them in the smallest unit (e.g. whole cents or the smallest currency subunit as an integer) to avoid floating-point precision issues during calculations.
12. Provide Stable Identification Fields
Beyond an internal numeric id, many modern APIs also expose fields like uuid or slug that are safer to expose publicly and don't reveal a predictable sequence (avoiding enumeration attacks, where someone can guess other resource IDs by incrementing a number). Also consider including createdAt and updatedAt by default on every resource, since clients almost always need them for cache invalidation or sorting purposes.