Postman Lesson 4 of 6 ⏱️ 4 min read

Schema & Contract Testing

Validating responses against a JSON Schema in Postman so a changed API contract fails a test immediately.

Updated Oct 30, 2026

Why validate the schema?

Checking a few fields (id, title) doesn't catch a renamed field, a number that became a string, or a field that suddenly disappears — exactly the changes that break mobile apps and front ends. A JSON Schema describes the full expected structure, and one assertion checks all of it.

This is contract testing in its simplest form: the API promises a shape, and your tests fail the moment it breaks that promise.

A JSON Schema in 60 seconds

JSON
{
  "type": "object",
  "required": ["id", "userId", "title", "body"],
  "properties": {
    "id":     { "type": "integer" },
    "userId": { "type": "integer" },
    "title":  { "type": "string", "minLength": 1 },
    "body":   { "type": "string" }
  },
  "additionalProperties": false
}
  • type — the expected JSON type.
  • required — fields that must be present.
  • properties — rules per field (types, lengths, ranges, formats, enums).
  • additionalProperties: false — fail if the API returns unexpected fields (strict contract).

Validating in Postman

Postman supports JSON Schema validation directly with jsonSchema:

Javascript
// GET {{baseUrl}}/posts/1   (baseUrl = https://jsonplaceholder.typicode.com)
const postSchema = {
  type: 'object',
  required: ['id', 'userId', 'title', 'body'],
  properties: {
    id: { type: 'integer' },
    userId: { type: 'integer' },
    title: { type: 'string', minLength: 1 },
    body: { type: 'string' },
  },
  additionalProperties: false,
};

pm.test('Post matches the contract', () => {
  pm.response.to.have.jsonSchema(postSchema);
});

When it fails, the test result says exactly which rule broke — e.g. data.userId should be integer.

Validating arrays

Javascript
// GET {{baseUrl}}/comments?postId=1
const commentListSchema = {
  type: 'array',
  minItems: 1,
  items: {
    type: 'object',
    required: ['postId', 'id', 'name', 'email', 'body'],
    properties: {
      postId: { type: 'integer' },
      id: { type: 'integer' },
      name: { type: 'string' },
      email: { type: 'string', format: 'email' },
      body: { type: 'string' },
    },
  },
};

pm.test('Comment list matches the contract', () => {
  pm.response.to.have.jsonSchema(commentListSchema);
});

Reusing schemas across requests

Define a schema once and reuse it: store it as a collection variable in a collection-level pre-request script.

Javascript
// Collection → Scripts → Pre-request
pm.collectionVariables.set('schema.post', JSON.stringify({
  type: 'object',
  required: ['id', 'userId', 'title', 'body'],
  properties: {
    id: { type: 'integer' },
    userId: { type: 'integer' },
    title: { type: 'string' },
    body: { type: 'string' },
  },
}));
Javascript
// Any request returning a post
const schema = JSON.parse(pm.collectionVariables.get('schema.post'));
pm.test('Matches post schema', () => pm.response.to.have.jsonSchema(schema));

Using the OpenAPI spec as the source of truth

If your API has an OpenAPI specification, import it into Postman (Import → OpenAPI file). Postman generates a collection with every endpoint and example. The schemas inside the spec become your contract — when the spec changes, regenerate and the tests follow.

Schema testing vs consumer-driven contract testing

Approach What it checks Tools
Schema validation The response matches the published structure Postman jsonSchema, Ajv, Playwright tests
Consumer-driven contracts The API satisfies what each consumer actually uses Pact

Schema validation in Postman catches most breaking changes with very little effort. For microservice architectures with many consumers, look into Pact — see Testing Microservices.

What to check beyond the schema

A response can match the schema and still be wrong. Combine schema tests with value tests:

Javascript
pm.test('Business rules', () => {
  const post = pm.response.json();
  pm.expect(post.id).to.eql(Number(pm.request.url.path.at(-1)));  // the id we asked for
  pm.expect(post.title.length).to.be.below(256);
});

Next: running the whole collection automatically with Data-Driven Runs & Newman.

Finished this lesson?
Tick it off to track your Postman progress.
Next lesson