
Security News
Next.js Patches Critical Middleware Vulnerability (CVE-2025-29927)
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
zod-to-json-schema
Advanced tools
The zod-to-json-schema npm package is a utility that converts Zod schemas to JSON Schema. This is useful for generating JSON Schema definitions from Zod validation schemas, which can then be used for various purposes such as API documentation, validation, and more.
Basic Schema Conversion
This feature allows you to convert a basic Zod schema to a JSON Schema. The example demonstrates converting a Zod object schema with string and integer properties to its JSON Schema equivalent.
const { z } = require('zod');
const { zodToJsonSchema } = require('zod-to-json-schema');
const schema = z.object({
name: z.string(),
age: z.number().int(),
});
const jsonSchema = zodToJsonSchema(schema);
console.log(JSON.stringify(jsonSchema, null, 2));
Nested Schema Conversion
This feature supports the conversion of nested Zod schemas to JSON Schema. The example shows a user schema that includes an address schema, demonstrating how nested objects are handled.
const { z } = require('zod');
const { zodToJsonSchema } = require('zod-to-json-schema');
const addressSchema = z.object({
street: z.string(),
city: z.string(),
});
const userSchema = z.object({
name: z.string(),
address: addressSchema,
});
const jsonSchema = zodToJsonSchema(userSchema);
console.log(JSON.stringify(jsonSchema, null, 2));
Array Schema Conversion
This feature allows for the conversion of Zod schemas that include arrays to JSON Schema. The example demonstrates converting a schema with an array of strings.
const { z } = require('zod');
const { zodToJsonSchema } = require('zod-to-json-schema');
const schema = z.object({
tags: z.array(z.string()),
});
const jsonSchema = zodToJsonSchema(schema);
console.log(JSON.stringify(jsonSchema, null, 2));
AJV (Another JSON Schema Validator) is a popular JSON Schema validator. While it focuses on validating JSON data against JSON Schema, it also provides utilities for generating JSON Schema from various sources. Compared to zod-to-json-schema, AJV is more focused on validation rather than schema conversion.
This package converts JSON Schema to TypeScript types. While it operates in the opposite direction of zod-to-json-schema, it serves a similar purpose of bridging the gap between JSON Schema and TypeScript. It is useful for generating TypeScript definitions from existing JSON Schemas.
This package generates JSON Schema from TypeScript types. It is similar to zod-to-json-schema in that it converts type definitions to JSON Schema, but it works directly with TypeScript types instead of Zod schemas.
Looking for the exact opposite? Check out json-schema-to-zod
Does what it says on the tin; converts Zod schemas into JSON schemas!
$ref
s.A great big thank you to our amazing sponsors! Please consider joining them through my GitHub Sponsors page. Every cent helps, but these fellas have really gone above and beyond 💚:
|
Cut code review time & bugs in half coderabbit.ai |
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
const mySchema = z
.object({
myString: z.string().min(5),
myUnion: z.union([z.number(), z.boolean()]),
})
.describe("My neat object schema");
const jsonSchema = zodToJsonSchema(mySchema, "mySchema");
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/mySchema",
"definitions": {
"mySchema": {
"description": "My neat object schema",
"type": "object",
"properties": {
"myString": {
"type": "string",
"minLength": 5
},
"myUnion": {
"type": ["number", "boolean"]
}
},
"additionalProperties": false,
"required": ["myString", "myUnion"]
}
}
}
You can pass a string as the second parameter of the main zodToJsonSchema function. If you do, your schema will end up inside a definitions object property on the root and referenced from there. Alternatively, you can pass the name as the name
property of the options object (see below).
Instead of the schema name (or nothing), you can pass an options object as the second parameter. The following options are available:
Option | Effect |
---|---|
name?: string | As described above. |
nameStrategy?: "ref" | "title" | Adds name as "title" meta instead of as a ref as described above |
basePath?: string[] | The base path of the root reference builder. Defaults to ["#"]. |
$refStrategy?: "root" | "relative" | "seen" | "none" | The reference builder strategy;
|
effectStrategy?: "input" | "any" | The effects output strategy. Defaults to "input". See known issues! |
dateStrategy?: "format:date" | "format:date-time" | "string" | "integer" | Date strategy, integer allow to specify in unix-time min and max values. "format:date" creates a string schema with format: "date". "format:date-time" creates a string schema with format: "date-time". "string" is intepreted as "format:date-time". "integer" creates an integer schema with format "unix-time" (unless target "openApi3" is used min max checks are also respected) |
emailStrategy?: "format:email" | "format:idn-email" | "pattern:zod" | Choose how to handle the email string check. Defaults to "format:email". |
base64Strategy?: "format:binary" | "contentEnconding:base64" | "pattern:zod" | Choose how to handle the base64 string check. Defaults to "contentEncoding:base64" as described here. Note that "format:binary" is not represented in the output type as it's not part of the JSON Schema spec and only intended to be used when targeting OpenAPI 3.0. Later versions of OpenAPI support contentEncoding. |
definitionPath?: "definitions" | "$defs" | The name of the definitions property when name is passed. Defaults to "definitions". |
target?: "jsonSchema7" | "jsonSchema2019-09" | "openApi3" | "openAi" | Which spec to target. Defaults to "jsonSchema7" |
strictUnions?: boolean | Scrubs unions of any-like json schemas, like {} or true . Multiple zod types may result in these out of necessity, such as z.instanceof() |
definitions?: Record<string, ZodSchema> | See separate section below |
errorMessages?: boolean | Include custom error messages created via chained function checks for supported zod types. See section below |
markdownDescription?: boolean | Copies the description meta to markdownDescription |
patternStrategy?: "escape" | "preserve" | The Zod string validations .includes() , .startsWith() , and .endsWith() must be converted to regex to be compatible with JSON Schema's pattern . For safety, all non-alphanumeric characters are escape d by default (consider z.string().includes(".") ), but this can occasionally cause problems with Unicode-flagged regex parsers. Use preserve to prevent this escaping behaviour and preserve the exact string written, even if it results in an inaccurate regex. |
applyRegexFlags?: boolean | JSON Schema's pattern doesn't support RegExp flags, but Zod's z.string().regex() does. When this option is true (default false), a best-effort is made to transform regexes into a flag-independent form (e.g. /x/i => /[xX]/ ). Supported flags: i (basic Latin only), m , s . |
pipeStrategy?: "all" | "input" | "output" | Decide which types should be included when using z.pipe , for example z.string().pipe(z.number()) would return both string and number by default, only string for "input" and only number for "output". |
removeAdditionalStrategy?: "passthrough" | "strict" | Decide when additionalProperties should be allowed. See the section on additional properties for details. |
allowedAdditionalProperties?: true | undefined | What value to give additionalProperties when allowed. See the section on additional properties for details. |
rejectedAdditionalProperties?: false | undefined | What value to give additionalProperties when rejected. See the section on additional properties for details. |
override?: callback | See section |
postProcess?: callback | See section |
The definitions option lets you manually add recurring schemas into definitions for cleaner outputs. It's fully compatible with named schemas, changed definitions path and base path. Here's a simple example:
const myRecurringSchema = z.string();
const myObjectSchema = z.object({ a: myRecurringSchema, b: myRecurringSchema });
const myJsonSchema = zodToJsonSchema(myObjectSchema, {
definitions: { myRecurringSchema },
});
{
"type": "object",
"properties": {
"a": {
"$ref": "#/definitions/myRecurringSchema"
},
"b": {
"$ref": "#/definitions/myRecurringSchema"
}
},
"definitions": {
"myRecurringSchema": {
"type": "string"
}
}
}
This feature allows optionally including error messages created via chained function calls for supported zod types:
// string schema with additional chained function call checks
const EmailSchema = z.string().email("Invalid email").min(5, "Too short");
const jsonSchema = zodToJsonSchema(EmailSchema, { errorMessages: true });
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"format": "email",
"minLength": 5,
"errorMessage": {
"format": "Invalid email",
"minLength": "Too short"
}
}
This allows for field specific, validation step specific error messages which can be useful for building forms and such. This format is accepted by react-hook-form
's ajv resolver (and therefor ajv-errors
which it uses under the hood). Note that if using AJV with this format will require enabling ajv-errors
as vanilla AJV does not accept this format by default.
By default, Zod removes undeclared properties when parsing object schemas. In order to replicate the expected output of this behaviour, the default for behaviour of zodToJsonSchema is to set "additionalProperties"
to false
(although the correctness of this can be debated). If you wish to allow undeclared properties you can either:
removeAdditionalStrategy
to "strict"
. This will allow additional properties for any object schema that is not declared with .strict()
.removeAdditionalStrategy
set to its default value of "passthrough"
, and add .passtrough()
to your object schema.additionalProperties
keyword using the allowedAdditionalProperties
and/or rejectedAdditionalProperties
options.Some schema definitions (like Googles Gen AI API for instance) does not allow the additionalProperties
keyword at all. Luckily the JSON Schema spec allows for this: leaving the keyword undefined should have the same effect as setting it to true (as per usual YMMV). To enable this behaviour, set the option allowedAdditionalProperties
to undefined
.
To exclude the keyword even when additional properties are not allowed, set the rejectedAdditionalProperties
to undefined
as well.
Heads up ⚠️: Both of these options will be ignored if your schema is declared with .catchall(...)
as the provided schema will be used instead (if valid).
z.object({}) + option | "additionalProperties" value |
---|---|
.strip() (default) | false if strategy is "passtrough" , true if "strict" |
.passtrough() | true |
.strict() | false |
.catchall(z.string()) | { "type": "string" } |
.catchall(z.function()) | undefined (function schemas are not currently parseable) |
Substitute true
and false
for undefined
according to allowedAdditionalProperties
and/or rejectedAdditionalProperties
respectively.
override
This options takes a callback receiving a Zod schema definition, the current reference object (containing the current ref path and other options), an argument containing inforation about wether or not the schema has been encountered before, and a forceResolution argument.
Important: if you don't want to override the current item you have to return the ignoreOverride
symbol exported from the index. This is because undefined
is a valid option to return when you want the property to be excluded from the resulting JSON schema.
import zodToJsonSchema, { ignoreOverride } from "zod-to-json-schema";
zodToJsonSchema(
z.object({
ignoreThis: z.string(),
overrideThis: z.string(),
removeThis: z.string(),
}),
{
override: (def, refs) => {
const path = refs.currentPath.join("/");
if (path === "#/properties/overrideThis") {
return {
type: "integer",
};
}
if (path === "#/properties/removeThis") {
return undefined;
}
// Important! Do not return `undefined` or void unless you want to remove the property from the resulting schema completely.
return ignoreOverride;
},
},
);
Expected output:
{
"type": "object",
"required": ["ignoreThis", "overrideThis"],
"properties": {
"ignoreThis": {
"type": "string"
},
"overrideThis": {
"type": "integer"
}
},
"additionalProperties": false
}
postProcess
Besided receiving all arguments of the override
callback, the postProcess
callback also receives the generated schema. It should always return a JSON Schema, or undefined
if you wish to filter it out. Unlike the override
callback you do not have to return ignoreOverride
if you are happy with the produced schema; simply return it unchanged.
import zodToJsonSchema, { PostProcessCallback } from "zod-to-json-schema";
// Define the callback to be used to process the output using the PostProcessCallback type:
const postProcess: PostProcessCallback = (
// The original output produced by the package itself:
jsonSchema,
// The ZodSchema def used to produce the original schema:
def,
// The refs object containing the current path, passed options, etc.
refs,
) => {
if (!jsonSchema) {
return jsonSchema;
}
// Try to expand description as JSON meta:
if (jsonSchema.description) {
try {
jsonSchema = {
...jsonSchema,
...JSON.parse(jsonSchema.description),
};
} catch {}
}
// Make all numbers nullable:
if ("type" in jsonSchema! && jsonSchema.type === "number") {
jsonSchema.type = ["number", "null"];
}
// Add the refs path, just because
(jsonSchema as any).path = refs.currentPath;
return jsonSchema;
};
const jsonSchema = zodToJsonSchema(zodSchema, { postProcess });
postProcess
for including examples and other metaAdding support for examples and other JSON Schema meta keys are among the most commonly requested features for this project. Unfortunately the current Zod major (3) has pretty anemic support for this, so some userland hacking is required. Since this is such a common usecase I've included a helper function that simply tries to parse any description as JSON and expand it into the resulting schema.
Simply stringify whatever you want added to the output schema as the description, then import and use jsonDescription
as the postProcess option:
import zodToJsonSchema, { jsonDescription } from "zod-to-json-schema";
const zodSchema = z.string().describe(
JSON.stringify({
title: "My string",
description: "My description",
examples: ["Foo", "Bar"],
whatever: 123,
}),
);
const jsonSchema = zodToJsonSchema(zodSchema, {
postProcess: jsonDescription,
});
Expected output:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "string",
"title": "My string",
"description": "My description",
"examples": ["Foo", "Bar"],
"whatever": 123
}
.transform
, the return type is inferred from the supplied function. In other words, there is no schema for the return type, and there is no way to convert it in runtime. Currently the JSON schema will therefore reflect the input side of the Zod schema and not necessarily the output (the latter aka. z.infer
). If this causes problems with your schema, consider using the effectStrategy "any", which will allow any type of output.z.record
with any other key type, this will be ignored. An exception to this rule is z.enum
as is supported since 3.11.3.isOptional()
to check if a property should be included in required
or not. This has the potentially dangerous behavior of calling .safeParse
with undefined
. To work around this, make sure your preprocess
and other effects callbacks are pure and not liable to throw errors. An issue has been logged in the Zod repo and can be tracked here.$schema
field to: "https://json-schema.org/draft/2020-12/schema#"This package does not follow semantic versioning. The major and minor versions of this package instead reflects feature parity with the Zod package.
I will do my best to keep API-breaking changes to an absolute minimum, but new features may appear as "patches", such as introducing the options pattern in 3.9.1.
https://github.com/StefanTerdell/zod-to-json-schema/blob/master/changelog.md
FAQs
Converts Zod schemas to Json Schemas
The npm package zod-to-json-schema receives a total of 3,038,303 weekly downloads. As such, zod-to-json-schema popularity was classified as popular.
We found that zod-to-json-schema demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Next.js has patched a critical vulnerability (CVE-2025-29927) that allowed attackers to bypass middleware-based authorization checks in self-hosted apps.
Security News
A survey of 500 cybersecurity pros reveals high pay isn't enough—lack of growth and flexibility is driving attrition and risking organizational security.
Product
Socket, the leader in open source security, is now available on Google Cloud Marketplace for simplified procurement and enhanced protection against supply chain attacks.