Intermediate Chapter 9 · 10 min read

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.

schema-validation.test.js
// 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!');
SchemaValidationTest.java
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
// }
test_schema_validation.py
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}')

API Testing Intermediate JSON Schema Validation

Written by PV

© 2026 All Rights Reserved