Skip to content

JSON

Design JSON structures that are consistent, predictable, and easy to work with across different programming languages and platforms.

Follow standard JSON syntax:

{
"name": "John Doe",
"age": 30,
"active": true,
"tags": ["developer", "designer"],
"address": {
"city": "San Francisco",
"country": "USA"
}
}

JSON supports six data types:

{
"string": "text value",
"number": 42,
"boolean": true,
"null": null,
"array": [1, 2, 3],
"object": { "key": "value" }
}

Use camelCase for property names:

{
"firstName": "John",
"lastName": "Doe",
"emailAddress": "john@example.com",
"phoneNumber": "+1234567890"
}

Use clear, descriptive names:

✅ {
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-20T14:45:00Z"
}
❌ {
"crtd": "2025-01-15T10:30:00Z",
"upd": "2025-01-20T14:45:00Z"
}

Avoid abbreviations unless widely recognized:

✅ {
"id": "123",
"url": "https://example.com",
"api": "https://api.example.com"
}
❌ {
"usr": "john",
"addr": "123 Main St",
"qty": 5
}

Always use ISO 8601 format in UTC:

{
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-20T14:45:00.123Z",
"publishedDate": "2025-01-15"
}

Use appropriate numeric types:

{
"price": 19.99,
"quantity": 5,
"percentage": 0.15,
"largeNumber": 1234567890
}

Avoid string representations for numbers:

❌ {
"price": "19.99",
"quantity": "5"
}

Use boolean types, not strings or numbers:

✅ {
"active": true,
"verified": false
}
❌ {
"active": "true",
"verified": 0
}

Include null for missing optional values, omit for truly absent data:

{
"name": "John Doe",
"middleName": null,
"email": "john@example.com"
}

Structure successful responses consistently:

{
"data": {
"id": "123",
"name": "Product Name",
"price": 29.99
},
"meta": {
"timestamp": "2025-01-15T10:30:00Z",
"version": "2.0"
}
}

Use consistent structure for collections:

{
"data": [
{
"id": "1",
"name": "Item 1"
},
{
"id": "2",
"name": "Item 2"
}
],
"meta": {
"total": 100,
"count": 2
},
"links": {
"self": "https://api.example.com/items?cursor=abc123",
"next": "https://api.example.com/items?cursor=def456"
}
}

Use RFC 7807 (Problem Details) format:

{
"type": "https://api.example.com/errors/validation",
"title": "Validation Failed",
"status": 400,
"detail": "The request body contains invalid data",
"instance": "/users/123",
"errors": [
{
"field": "email",
"message": "Invalid email format"
}
]
}

Provide actionable error information:

{
"error": {
"code": "INVALID_EMAIL",
"message": "The email address provided is invalid",
"field": "email",
"documentation": "https://api.example.com/docs/errors/invalid-email"
}
}

Use cursor-based pagination for performance:

{
"data": [...],
"pagination": {
"cursor": "eyJpZCI6MTIzfQ==",
"hasMore": true,
"limit": 20
},
"links": {
"next": "https://api.example.com/items?cursor=eyJpZCI6MTIzfQ==&limit=20"
}
}

For simpler use cases, offset pagination is acceptable:

{
"data": [...],
"pagination": {
"offset": 0,
"limit": 20,
"total": 100
}
}

Use nested objects for related data:

{
"user": {
"id": "123",
"name": "John Doe",
"profile": {
"bio": "Software developer",
"avatar": "https://example.com/avatar.jpg"
},
"settings": {
"notifications": true,
"theme": "dark"
}
}
}

Keep nesting reasonable (3-4 levels max):

❌ {
"level1": {
"level2": {
"level3": {
"level4": {
"level5": {
"data": "too deep"
}
}
}
}
}
}

Prefer versioning in URL:

GET /v2/users/123
{
"data": {
"id": "123",
"name": "John Doe"
}
}

Alternative: use Accept header:

Accept: application/vnd.api.v2+json

Avoid including version in JSON payload - it’s redundant.

Use consistent format for links:

{
"data": {...},
"links": {
"self": "https://api.example.com/users/123",
"related": {
"posts": "https://api.example.com/users/123/posts",
"comments": "https://api.example.com/users/123/comments"
}
}
}

Include hypermedia links for discoverability:

{
"data": {
"id": "123",
"status": "draft"
},
"actions": {
"publish": {
"href": "/articles/123/publish",
"method": "POST"
},
"delete": {
"href": "/articles/123",
"method": "DELETE"
}
}
}

Define and validate structure:

{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "integer",
"minimum": 0
}
},
"required": ["name"]
}

Document APIs with OpenAPI specification:

openapi: 3.0.0
paths:
/users:
get:
responses:
'200':
content:
application/json:
schema:
type: object

For configuration, consider JSON5/JSONC with comments:

{
// Application configuration
"name": "My App",
"version": "1.0.0",
"features": {
"auth": true, // Enable authentication
"logging": false, // Disable logging in production
},
}

✅ Use camelCase for property names ✅ Use ISO 8601 for dates ✅ Include proper error messages ✅ Use cursor-based pagination ✅ Keep structures logical and consistent ✅ Validate with JSON Schema ✅ Document with OpenAPI ✅ Use proper HTTP status codes ✅ Include helpful links and metadata

❌ Use string representations for numbers/booleans ❌ Include version in JSON payload ❌ Use page-based pagination for large datasets ❌ Nest objects excessively deep ❌ Use cryptic abbreviations ❌ Ignore standard media types ❌ Mix naming conventions ❌ Return HTML in error responses

Use standard media types:

  • application/json - Standard JSON
  • application/problem+json - RFC 7807 errors
  • application/hal+json - HAL format
  • application/vnd.api+json - JSON:API spec

For versioned APIs:

application/vnd.myapp.v2+json
  • JSON Schema - Structure validation
  • AJV - Fast JSON Schema validator
  • Joi - Object schema validation
  • OpenAPI - API specification
  • Swagger UI - Interactive documentation
  • Redoc - API documentation
  • prettier - Formatting
  • eslint-plugin-json - JSON linting
  • jsonlint - JSON validation