Common JSON Validation Patterns for Robust APIs
Robust API design requires thorough validation at every layer. This article explores common validation patterns that keep your API reliable and your data clean.
The Validation Pyramid
Structure your validation in layers:
- Syntax Validation: Is it valid JSON?
- Schema Validation: Does it match the structure?
- Business Logic Validation: Does it make sense?
- Referential Validation: Do references exist?
Required Field Validation
Ensure critical fields are present:
{
"type": "object",
"properties": {
"email": { "type": "string" },
"password": { "type": "string" }
},
"required": ["email", "password"]
}
Type-Safe Validation
Always validate types explicitly:
{
"userId": { "type": "integer", "minimum": 1 },
"score": { "type": "number", "minimum": 0, "maximum": 100 },
"isActive": { "type": "boolean" },
"tags": { "type": "array", "items": { "type": "string" } }
}
String Format Validation
Use built-in formats for common patterns:
{
"email": { "type": "string", "format": "email" },
"uri": { "type": "string", "format": "uri" },
"date-time": { "type": "string", "format": "date-time" },
"uuid": { "type": "string", "format": "uuid" },
"hostname": { "type": "string", "format": "hostname" },
"ipv4": { "type": "string", "format": "ipv4" }
}
Enum and Const Values
Restrict to specific values:
{
"status": {
"type": "string",
"enum": ["pending", "approved", "rejected"]
},
"priority": {
"type": "string",
"const": "high"
}
}
Array Validation Patterns
Validate array contents and structure:
{
"items": {
"type": "array",
"items": { "type": "object" },
"minItems": 1,
"maxItems": 100,
"uniqueItems": true
}
}
For heterogeneous arrays:
{
"tuple": {
"type": "array",
"items": [
{ "type": "string" },
{ "type": "number" },
{ "type": "boolean" }
]
}
}
Conditional Validation
Validate based on other field values:
{
"type": "object",
"properties": {
"type": { "type": "string", "enum": ["personal", "business"] },
"companyName": { "type": "string" },
"ssn": { "type": "string" }
},
"if": {
"properties": { "type": { "const": "business" } }
},
"then": {
"required": ["companyName"]
},
"else": {
"required": ["ssn"]
}
}
Pattern Matching
Use regex for custom string validation:
{
"username": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{2,15}$",
"minLength": 3,
"maxLength": 16
},
"phone": {
"type": "string",
"pattern": "^\\+?[1-9]\\d{1,14}$"
}
}
Numeric Range Validation
Prevent overflow and invalid values:
{
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 1000
},
"discount": {
"type": "number",
"minimum": 0,
"maximum": 1,
"multipleOf": 0.01
}
}
Nested Object Validation
Validate complex structures:
{
"type": "object",
"properties": {
"address": {
"type": "object",
"required": ["street", "city", "country"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zipcode": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
}
}
},
"required": ["address"]
}
Error Message Best Practices
Return helpful validation errors:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "must be a valid email address",
"received": "invalid-email"
},
{
"field": "age",
"message": "must be >= 18",
"received": 16
}
]
}
}
Summary
These validation patterns form the foundation of reliable APIs. Combine them thoughtfully to create robust, self-documenting interfaces that catch errors early and provide clear feedback to clients.