Knowing the basic keywords is not enough. You need to know how to constrain each data type. This is where schema validation becomes powerful — you do not just check "is this a string?" but "is this a string that looks like an email and has at least 5 characters?"
{
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"format": "email"
},
"phone": {
"type": "string",
"pattern": "^[0-9]{10}$"
},
"status": {
"type": "string",
"enum": ["active", "inactive", "suspended"]
}
}| Constraint | What It Does | Example Value |
|---|---|---|
| minLength | Minimum number of characters | "minLength": 1 — rejects empty strings |
| maxLength | Maximum number of characters | "maxLength": 100 — rejects very long strings |
| pattern | Must match a regex pattern | "pattern": "^[0-9]{10}$" — exactly 10 digits |
| format | Built-in format check | "format": "email", "date-time", "uri", "ipv4" |
| enum | Must be one of the listed values | "enum": ["active", "inactive"] — nothing else allowed |
The "pattern" keyword uses regular expressions. For Indian phone numbers, use "^[6-9][0-9]{9}$". For PAN card: "^[A-Z]{5}[0-9]{4}[A-Z]$". Pattern is your most powerful string constraint.
{
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
},
"price": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": 0
},
"rating": {
"type": "number",
"minimum": 1,
"maximum": 5,
"multipleOf": 0.5
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 100
}
}| Constraint | What It Does | Note |
|---|---|---|
| minimum | Value must be >= this number | "minimum": 0 — no negative values |
| maximum | Value must be <= this number | "maximum": 150 — upper bound |
| exclusiveMinimum | Value must be > this number (not equal) | "exclusiveMinimum": 0 — rejects 0, allows 0.01 |
| exclusiveMaximum | Value must be < this number | "exclusiveMaximum": 100 — rejects 100, allows 99.99 |
| multipleOf | Value must be divisible by this | "multipleOf": 0.5 — allows 1.0, 1.5, 2.0 |
In JSON Schema, "integer" and "number" are different types. "integer" means whole numbers only (1, 2, 3). "number" allows decimals (1.5, 2.99). If the API returns price as 99.99 and your schema says "type": "integer" — it will fail.
{
"isActive": {
"type": "boolean"
},
"deletedAt": {
"type": ["string", "null"]
}
}
// "isActive": true ← VALID
// "isActive": "true" ← INVALID — string, not boolean
// "deletedAt": null ← VALID — null is allowed
// "deletedAt": "2024-01-15" ← VALID — string is allowedNotice the "type": ["string", "null"] syntax. This means the field can be either a string or null. This is how you handle nullable fields. Very common in real APIs.
{
"tags": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
},
"scores": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 100
}
},
"users": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
}
}
}
}| Constraint | What It Does | Example |
|---|---|---|
| items | Schema that each item in the array must match | "items": { "type": "string" } |
| minItems | Minimum number of items in the array | "minItems": 1 — array cannot be empty |
| maxItems | Maximum number of items | "maxItems": 10 — cap the array size |
| uniqueItems | All items must be unique | "uniqueItems": true — no duplicates |
Enum is not a data type. It is a constraint that works with any type. Use it when a field can only have specific values.
{
"accountType": {
"type": "string",
"enum": ["savings", "current", "fixed_deposit"]
},
"priority": {
"type": "integer",
"enum": [1, 2, 3, 4, 5]
},
"status": {
"type": "string",
"enum": ["pending", "approved", "rejected", "cancelled"]
}
}Key Point: Enum is your best friend for fields with fixed values like status, type, category, or role. If the API returns "APPROVED" but your enum says "approved" — it will fail. Case matters.
Q: How would you validate that an API response field is a 10-digit Indian phone number using JSON Schema?
A: Use the "pattern" keyword with a regex: { "type": "string", "pattern": "^[6-9][0-9]{9}$" }. This ensures the value is a string, starts with 6-9 (valid Indian mobile prefix), and has exactly 10 digits total. You could also add "minLength": 10 and "maxLength": 10 as extra safety, but the pattern alone is sufficient.
Key Point: Each data type has its own set of constraints — strings have pattern and enum, numbers have min/max, arrays have items and minItems. Use them to make your schemas precise, not just type-safe.