Skip to main content
AllDevToolsHub
2026-05-02
Last reviewed: May 2026
JSON
Est Read: 07_MIN

JSON Schema Validation: Stop Trusting API Responses Blindly

JSON Schema Validation: Stop Trusting API Responses Blindly
Processing_Node: 01

#1JSON Schema validation for API responses and model output

What we tested: We processed API response payloads ranging from 1 KB to 50 MB through each JSON tool on this site. Formatting, validation, and conversion times were measured in Chrome 128 with DevTools performance panel. All processing runs locally in the browser, no server round-trips.

JSON Schema matters because external data can change even when your code does not.

The practical benefit is runtime validation: check API responses, service boundaries, and model output before bad data makes it into the rest of your app.


#2Why Runtime Validation Is Non-Negotiable in 2026

TypeScript gives you static types. But static types only apply to code you wrote, not to data that arrives from outside your process. Three sources of unvalidated data cause the majority of production incidents:

  1. Third-party APIs, providers change their response shape, add breaking fields, or deprecate properties without notice.
  2. Your own microservices, service A deploys a new field; service B was never updated to handle it. Both pass TypeScript compilation.
  3. LLM output, AI models are instructed to return JSON but frequently add extra fields, omit required ones, or change nesting structure between responses.

Runtime validation catches all three. TypeScript cannot.


#2JSON Schema Basics

JSON Schema describes the structure of a JSON document. Here is a minimal example:

json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["id", "email", "role"],
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "viewer"]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "additionalProperties": false
}

This schema says: the object must have id (positive integer), email (valid email string), and role (one of three values). createdAt is optional. No additional properties are allowed.


#2Draft-07 vs 2020-12, Which to Use

FeatureDraft-072020-12
Browser supportUniversalModern validators only
$ref + sibling keywordsNot allowedAllowed
unevaluatedProperties✅ (stricter additionalProperties)
prefixItems (for arrays)Uses items
Vocabulary system
Ajv supportFullFull (v8+)
Zod exportN/AN/A (Zod generates its own format)

Recommendation for 2026: Use 2020-12 for new projects, it fixes long-standing ambiguities in additionalProperties and $ref. Use draft-07 only if you need compatibility with older validators or the OpenAPI 3.0 ecosystem (which still uses draft-07 internally).


#2Validator Libraries, Zod vs Ajv vs Yup

#3Zod, TypeScript-first, single source of truth

typescript
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number().int().positive(),
  email: z.string().email(),
  role: z.enum(['admin', 'user', 'viewer']),
  createdAt: z.string().datetime().optional(),
});

// TypeScript type inferred automatically, no duplication
type User = z.infer<typeof UserSchema>;

// Validate
const result = UserSchema.safeParse(apiResponse);
if (result.success) {
  const user = result.data; // User
} else {
  console.error(result.error.flatten());
  // { fieldErrors: { email: ['Invalid email'] }, formErrors: [] }
}

Zod pros: TypeScript type + runtime validator from one definition, excellent error messages, composable, no separate JSON Schema file needed.
Zod cons: Bundle size (~13 KB minzipped), cannot import/export standard JSON Schema directly.


#3Ajv, fastest, JSON Schema compliant

javascript
import Ajv from 'ajv';
import addFormats from 'ajv-formats'; // for 'email', 'date-time', etc.

const ajv = new Ajv({ allErrors: true }); // collect all errors, not just first
addFormats(ajv);

const userSchema = {
  type: 'object',
  required: ['id', 'email', 'role'],
  properties: {
    id: { type: 'integer', minimum: 1 },
    email: { type: 'string', format: 'email' },
    role: { type: 'string', enum: ['admin', 'user', 'viewer'] },
  },
  additionalProperties: false,
};

const validate = ajv.compile(userSchema);

function validateUser(data: unknown) {
  if (validate(data)) {
    return data as User; // valid, safe cast
  }
  throw new Error(
    `Invalid user: ${ajv.errorsText(validate.errors)}`
  );
}

Ajv pros: Fastest validator (compiled to JavaScript functions), full JSON Schema support, used by many tools that accept JSON Schema externally.
Ajv cons: Does not generate TypeScript types, you maintain schema and type separately, or use json-schema-to-typescript for codegen.


#3Yup, form validation roots, but general-purpose

javascript
import * as yup from 'yup';

const userSchema = yup.object({
  id: yup.number().integer().positive().required(),
  email: yup.string().email().required(),
  role: yup.mixed().oneOf(['admin', 'user', 'viewer']).required(),
});

try {
  const user = await userSchema.validate(apiResponse, { abortEarly: false });
} catch (err) {
  if (err instanceof yup.ValidationError) {
    console.error(err.errors); // array of error messages
  }
}

Yup pros: Familiar API, good for form validation, async validation built-in.
Yup cons: Slower than Ajv, TypeScript support weaker than Zod, less popular for API validation in 2026.


#2When to Use Each

ScenarioBest choice
TypeScript app, full control of schemaZod
Sharing schema with non-TypeScript servicesAjv with JSON Schema files
OpenAPI/Swagger integrationAjv (compatible schema format)
Form validation in ReactZod + react-hook-form or Yup + Formik
LLM output validationZod (great error messages for debugging)
Maximum performance (hot path)Ajv (compiled validators)

#2Validating LLM Output. The 2026 Use Case

Language models are instructed to return JSON but do not guarantee it. They add commentary before the JSON, nest it unexpectedly, or omit required fields. Always validate:

typescript
import { z } from 'zod';
import OpenAI from 'openai';

const ProductSchema = z.object({
  name: z.string().min(1).max(100),
  price: z.number().nonnegative(),
  category: z.enum(['electronics', 'clothing', 'books', 'food']),
  inStock: z.boolean(),
  tags: z.array(z.string()).max(10),
});

type Product = z.infer<typeof ProductSchema>;

async function extractProduct(text: string): Promise<Product> {
  const client = new OpenAI();
  
  const completion = await client.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: 'Extract product information as JSON. Return only valid JSON, nothing else.',
      },
      { role: 'user', content: text },
    ],
    response_format: { type: 'json_object' }, // GPT-4o JSON mode
  });

  const raw = completion.choices[0].message.content;
  
  // Even with JSON mode, validate the structure
  const parsed = JSON.parse(raw ?? '{}');
  const result = ProductSchema.safeParse(parsed);
  
  if (!result.success) {
    throw new Error(
      `LLM returned invalid product schema: ${JSON.stringify(result.error.flatten())}`
    );
  }
  
  return result.data;
}

#2CI Pipeline Integration

Run schema validation in CI before deployment to catch API contract drift:

yaml
# .github/workflows/api-contract-test.yml
name: API Contract Tests
on: [push, pull_request]

jobs:
  contract-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm ci
      - name: Validate API responses against schemas
        run: npm run test:contract
typescript
// tests/contract/users-api.test.ts
import { describe, it, expect } from 'vitest';
import { UserSchema } from '@/schemas/user';

describe('Users API contract', () => {
  it('GET /api/users returns valid user array', async () => {
    const response = await fetch('http://localhost:3000/api/users');
    const data = await response.json();
    
    expect(Array.isArray(data)).toBe(true);
    data.forEach((item: unknown, i: number) => {
      const result = UserSchema.safeParse(item);
      expect(result.success, `User at index ${i} failed validation: ${JSON.stringify(result)}`).toBe(true);
    });
  });
});

#2Try It In Your Browser

Paste any JSON and a JSON Schema into the AllDevToolsHub JSON Schema Validator to instantly see which fields fail, with human-readable error messages, supports both draft-07 and 2020-12. No data leaves your browser.


#2Frequently Asked Questions

#3What is the difference between JSON Schema and TypeScript interfaces?

TypeScript interfaces are compile-time constructs, they exist in your source code and are erased at runtime. JSON Schema is a runtime document, it describes valid JSON and can be used to validate actual data. TypeScript interfaces tell the compiler what type something should be. JSON Schema tells your application what shape data actually is at runtime.

#3Should I use Zod or JSON Schema?

Depends on your stack. Zod is better when you work entirely in TypeScript and want the type and validator from one definition. JSON Schema is better when you need to share the schema across different languages or services, or when you need OpenAPI compatibility. Many teams use Zod internally and export to JSON Schema format using zod-to-json-schema for external consumers.

#3Can Ajv validate 2020-12 schemas?

Yes, Ajv v8+ supports 2020-12. Install ajv v8+ and ajv-formats, then instantiate with new Ajv2020() from ajv/dist/2020 instead of the default import.

#3How do I convert an existing TypeScript type to a Zod schema?

You cannot automatically convert TypeScript types to Zod (TypeScript types are erased at compile time). Go the other direction: write Zod schemas first, then infer the TypeScript type with z.infer<typeof MySchema>. For existing types, you write the Zod schema manually once.

#3What happens when my JSON Schema has additionalProperties: false but the API adds a new field?

Validation fails. This is a trade-off: strict schemas catch unexpected changes early, but require updating the schema when the API intentionally adds fields. For APIs you control, additionalProperties: false is safe. For third-party APIs where you have no control over additions, use additionalProperties: true (or just omit it) and only validate the fields you care about.

Written by Rahul Jalavadiya, founder of AllDevToolsHub. All tools run locally in your browser.

#2Sources / Further reading

#2Try These Tools

Quick Summary

>- A practical guide to JSON Schema validation in 2026. Covers JSON Schema draft-07 vs 2020-12, Zod vs Ajv vs Yup trade-offs, validating API responses and LLM output at runtime, and integrating schema checks into your CI pipeline and TypeScript workflow.

RJRahul JalavadiyaFounder & Lead Engineer
Published 2026-05-02Last reviewed 2026-05-02

Tools Mentioned in This Article

Tools, tactics, and toughened-up tips, once a week

New tools, deep-dives on developer workflows, and the occasional gem we found this week. No spam, no tracking. Unsubscribe anytime.

Found an error or have feedback?

We correct errors quickly and document changes in our changelog. Report issues at support@alldevtoolshub.com.

Last reviewed: 2026-05-02
Security Memo
AT

Rahul Jalavadiya

Engineering Protocol V1

Specializing in local-first architecture and Zero-Trust developer workflows. No data leaves the machine.