JSON Schema Validation
So far you've asserted on individual fields one at a time: check the status code, check
id, check title. That works fine for a small response, but a real
RecipeBox Recipe object has nested arrays, optional fields, and specific types for
each key — writing a separate pm.expect() line for every one of them is tedious and
still leaves gaps (what if ingredients comes back as a string instead of an array, and
none of your field checks happen to catch it?). JSON Schema lets you describe the
entire shape of a response once, then validate the whole thing against that description in a
single assertion.
Why Field-by-Field Assertions Don't Scale
| Field-by-field assertions | JSON Schema validation |
|---|---|
One pm.expect() line per field — grows with every field you add to the Recipe model | One schema, one assertion, covers the entire object |
| Easy to forget a field, or forget to check its type | The schema is the single source of truth for required fields and types |
| Doesn't catch unexpected extra fields or wrong array item types | Can constrain array item shape, enums, and (optionally) reject unknown properties |
| Breaks silently one field at a time as the API evolves | Breaks loudly and precisely — the schema mismatch tells you exactly what changed |
Writing a Schema for the Recipe Object
A RecipeBox Recipe has id, title, ingredients
(an array of strings), steps (an array of strings), photoUri,
category, prepMinutes, and isPublished. Here's a JSON
Schema describing that shape, saved as a variable or a file you can reuse across requests:
{
"type": "object",
"required": ["id", "title", "ingredients", "steps", "category", "prepMinutes", "isPublished"],
"properties": {
"id": { "type": "number" },
"title": { "type": "string" },
"ingredients": {
"type": "array",
"items": { "type": "string" }
},
"steps": {
"type": "array",
"items": { "type": "string" }
},
"photoUri": { "type": ["string", "null"] },
"category": { "type": "string" },
"prepMinutes": { "type": "number", "minimum": 0 },
"isPublished": { "type": "boolean" }
}
}
Note photoUri allows "string" or "null" — a draft recipe
with no photo yet is still a valid Recipe, so the schema should say so rather than forcing every
recipe to have a photo.
Validating a Response Against the Schema
On the Get Recipe by ID request (GET
{{baseUrl}}/recipes/42), paste the schema into a variable in the
Tests tab and run it through pm.response.to.have.jsonSchema(), which Postman exposes
using an Ajv-based validator under the hood:
var recipeSchema = {
"type": "object",
"required": ["id", "title", "ingredients", "steps", "category", "prepMinutes", "isPublished"],
"properties": {
"id": { "type": "number" },
"title": { "type": "string" },
"ingredients": { "type": "array", "items": { "type": "string" } },
"steps": { "type": "array", "items": { "type": "string" } },
"photoUri": { "type": ["string", "null"] },
"category": { "type": "string" },
"prepMinutes": { "type": "number", "minimum": 0 },
"isPublished": { "type": "boolean" }
}
};
pm.test("response matches the Recipe schema", function () {
pm.response.to.have.status(200);
pm.expect(pm.response.json()).to.be.jsonSchema(recipeSchema);
});
If a field is missing, has the wrong type, or a required key is absent, this single test fails with a message naming exactly which part of the schema didn't match — far more useful than five separate green/red checkmarks with no context connecting them.
Older Postman Versions and the tv4 Library
Older Postman sandbox versions exposed the tv4 library directly for schema
validation. You may still see this pattern in older collections or tutorials:
var jsonData = pm.response.json();
var result = tv4.validate(jsonData, recipeSchema);
pm.test("response matches Recipe schema (tv4)", function () {
pm.expect(result).to.be.true;
});
Current Postman versions favor pm.response.to.have.jsonSchema() (Ajv-based) since it
gives clearer validation error messages and supports more of the JSON Schema specification. Prefer
it for anything you're writing today; recognizing the tv4 pattern is mainly useful for
reading existing collections.
isPublished is false by default") since a schema only checks shape and type, not business logic.