Ajv standalone validation code generated at build time: working versions (tested 2026-10-15)
Compile JSON Schemas once with Ajv into standalone ESM validator modules, write them to disk and import them at runtime without compiling, checking errors, defaults and type coercion.
passed4 passed · 0 failed2026-10-15 17:10 UTC- Status
- passed
- Last passed
- 2026-10-15 17:10 UTC
- Tests
- 4 / 4
- Language
- TypeScript
- Runtime
- node v22.23.3
npm install --no-audit --no-fund --ignore-scripts=false npm test
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38775053802
The code
Exactly the files that passed. Your agent can pull them with get_recipe("ajv-standalone-validation") (install), or see them on GitHub.
{
"name": "ajv-standalone-validation",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"ajv": "8.20.0"
},
"devDependencies": {
"@types/node": "26.6.5",
"tsx": "4.23.15"
}
}
import fs from "node:fs";
import path from "node:path";
import Ajv from "ajv";
import standaloneCode from "ajv/dist/standalone/index.js";
export const userSchema = {
$id: "user",
type: "object",
required: ["email", "age"],
additionalProperties: false,
properties: {
email: { type: "string", pattern: "^[^@\\s]+@[^@\\s]+$" },
age: { type: "integer", minimum: 0 },
plan: { type: "string", enum: ["free", "pro"], default: "free" },
},
} as const;
export const querySchema = {
$id: "query",
type: "object",
properties: { page: { type: "integer", minimum: 1, default: 1 }, q: { type: "string" } },
} as const;
/** Generates one ESM module exporting a validator per schema $id. */
export function buildValidators(outFile: string): string {
const ajv = new Ajv({
schemas: [userSchema, querySchema],
allErrors: true,
useDefaults: true,
coerceTypes: true,
code: { source: true, esm: true },
});
const code = standaloneCode(ajv, { validateUser: "user", validateQuery: "query" });
fs.mkdirSync(path.dirname(outFile), { recursive: true });
fs.writeFileSync(outFile, code);
return code;
}
import { test, before } from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { buildValidators } from "../src/build.ts";
type Validator = ((data: unknown) => boolean) & { errors?: Array<{ instancePath: string; keyword: string }> | null };
let validateUser: Validator;
let validateQuery: Validator;
let code = "";
before(async () => {
// Written inside the project so the generated module can resolve ajv runtime helpers if it needs them.
const out = path.resolve("generated/validators.mjs");
code = buildValidators(out);
const mod = await import(pathToFileURL(out).href);
validateUser = mod.validateUser;
validateQuery = mod.validateQuery;
});
test("generated module is plain ESM without a schema compiler", () => {
assert.match(code, /export const validateUser/);
assert.match(code, /export const validateQuery/);
assert.doesNotMatch(code, /new Ajv/);
});
test("valid data passes and gets defaults", () => {
const user: Record<string, unknown> = { email: "[email protected]", age: 36 };
assert.equal(validateUser(user), true);
assert.equal(user.plan, "free");
});
test("allErrors reports every problem", () => {
assert.equal(validateUser({ email: "nope", age: -1, extra: true }), false);
const keywords = validateUser.errors!.map((e) => e.keyword).sort();
assert.deepEqual(keywords, ["additionalProperties", "minimum", "pattern"]);
});
test("coerces query string values", () => {
const query: Record<string, unknown> = { page: "3" };
assert.equal(validateQuery(query), true);
assert.equal(query.page, 3);
const empty: Record<string, unknown> = {};
assert.equal(validateQuery(empty), true);
assert.equal(empty.page, 1);
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
AJV 8 JSON Schema validation with ajv-formats
Compile JSON Schema drafts with AJV 8, enable email/uri/uuid formats via ajv-formats, and return field errors from validate.errors for API payloads.
Zod v4 schemas exported to JSON Schema and checked with Ajv
Export Zod v4 schemas to JSON Schema 2020-12 with z.toJSONSchema, using .meta() ids and descriptions and input vs output modes, then validate the same data with Ajv 2020.