JSON Schema Validation
Validate API response structure using JSON Schema — ensuring fields, types, required properties, and nested objects conform to a contract.
JSON Schema Validation
JSON Schema defines the structure your API responses should follow — required fields, data types, allowed values, and nested object shapes. It acts as a contract between the API and its consumers.
Why Schema Validation?
Checking individual fields is tedious and incomplete. A schema catches structural changes automatically: a missing field, a changed data type, or an unexpected new property. It's especially valuable when APIs evolve over time.
Schema Components
A JSON Schema defines type (object, array, string, number, etc.), properties with their own types, required fields that must be present, and constraints like minLength, minimum, enum, and pattern.
// Using Ajv — the fastest JSON Schema validator for JS
// npm install ajv
import Ajv from 'ajv';
const ajv = new Ajv();
// Define the schema for a Post
const postSchema = {
type: 'object',
required: ['id', 'userId', 'title', 'body'],
properties: {
id: { type: 'integer' },
userId: { type: 'integer' },
title: { type: 'string', minLength: 1 },
body: { type: 'string', minLength: 1 }
},
additionalProperties: false
};
// Define schema for a User (with nested objects)
const userSchema = {
type: 'object',
required: ['id', 'name', 'email', 'address'],
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string', format: 'email' },
address: {
type: 'object',
required: ['street', 'city', 'zipcode'],
properties: {
street: { type: 'string' },
city: { type: 'string' },
zipcode: { type: 'string' },
geo: {
type: 'object',
properties: {
lat: { type: 'string' },
lng: { type: 'string' }
}
}
}
}
}
};
// Validate a single post
const postResp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await postResp.json();
const validatePost = ajv.compile(postSchema);
console.assert(validatePost(post), 'Post should match schema');
// Validate a user with nested objects
const userResp = await fetch('https://jsonplaceholder.typicode.com/users/1');
const user = await userResp.json();
const validateUser = ajv.compile(userSchema);
const valid = validateUser(user);
if (!valid) console.log('Errors:', validateUser.errors);
// Validate array of items
const postsResp = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await postsResp.json();
const arraySchema = { type: 'array', items: postSchema, minItems: 1 };
const validateArray = ajv.compile(arraySchema);
console.assert(validateArray(posts), 'Posts array should match schema');
console.log('All schema validations passed!');
import static io.restassured.RestAssured.*;
import static io.restassured.module.jsv.JsonSchemaValidator.*;
import org.testng.annotations.Test;
public class SchemaValidationTest {
private static final String BASE = "https://jsonplaceholder.typicode.com";
@Test
public void testPostSchema() {
// Schema file: src/test/resources/post-schema.json
given()
.baseUri(BASE)
.when()
.get("/posts/1")
.then()
.statusCode(200)
.body(matchesJsonSchemaInClasspath("post-schema.json"));
}
@Test
public void testUserSchema() {
given()
.baseUri(BASE)
.when()
.get("/users/1")
.then()
.statusCode(200)
.body(matchesJsonSchemaInClasspath("user-schema.json"));
}
@Test
public void testPostsArraySchema() {
given()
.baseUri(BASE)
.when()
.get("/posts")
.then()
.statusCode(200)
.body(matchesJsonSchemaInClasspath("posts-array-schema.json"));
}
}
// --- src/test/resources/post-schema.json ---
// {
// "$schema": "http://json-schema.org/draft-07/schema#",
// "type": "object",
// "required": ["id", "userId", "title", "body"],
// "properties": {
// "id": { "type": "integer" },
// "userId": { "type": "integer" },
// "title": { "type": "string", "minLength": 1 },
// "body": { "type": "string", "minLength": 1 }
// },
// "additionalProperties": false
// }
import requests
from jsonschema import validate, ValidationError
BASE_URL = 'https://jsonplaceholder.typicode.com'
# Define schemas
POST_SCHEMA = {
'type': 'object',
'required': ['id', 'userId', 'title', 'body'],
'properties': {
'id': {'type': 'integer'},
'userId': {'type': 'integer'},
'title': {'type': 'string', 'minLength': 1},
'body': {'type': 'string', 'minLength': 1}
},
'additionalProperties': False
}
USER_SCHEMA = {
'type': 'object',
'required': ['id', 'name', 'email', 'address'],
'properties': {
'id': {'type': 'integer'},
'name': {'type': 'string'},
'email': {'type': 'string', 'format': 'email'},
'address': {
'type': 'object',
'required': ['street', 'city', 'zipcode'],
'properties': {
'street': {'type': 'string'},
'city': {'type': 'string'},
'zipcode': {'type': 'string'},
'geo': {
'type': 'object',
'properties': {
'lat': {'type': 'string'},
'lng': {'type': 'string'}
}
}
}
}
}
}
def test_post_schema():
"""Validate post matches JSON schema"""
response = requests.get(f'{BASE_URL}/posts/1')
post = response.json()
validate(instance=post, schema=POST_SCHEMA) # Raises on failure
def test_user_schema():
"""Validate user with nested objects"""
response = requests.get(f'{BASE_URL}/users/1')
user = response.json()
validate(instance=user, schema=USER_SCHEMA)
def test_posts_array_schema():
"""Validate array of posts"""
response = requests.get(f'{BASE_URL}/posts')
posts = response.json()
array_schema = {
'type': 'array',
'items': POST_SCHEMA,
'minItems': 1
}
validate(instance=posts, schema=array_schema)
def test_invalid_schema_fails():
"""Verify schema validation catches issues"""
bad_data = {'id': 'not-a-number', 'title': ''}
try:
validate(instance=bad_data, schema=POST_SCHEMA)
assert False, 'Should have raised ValidationError'
except ValidationError as e:
print(f'Caught expected error: {e.message}')
Written by PV
© 2026 All Rights Reserved