Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

zod-to-json-schema

StefanTerdell8.4mISC3.24.1TypeScript support: included

Converts Zod schemas to Json Schemas

zod, json, schema, open, api, conversion

readme

Zod to Json Schema

NPM Version NPM Downloads

Looking for the exact opposite? Check out json-schema-to-zod

Summary

Does what it says on the tin; converts Zod schemas into JSON schemas!

  • Supports all relevant schema types, basic string, number and array length validations and string patterns.
  • Resolves recursive and recurring schemas with internal $refs.
  • Supports targeting legacy Open API 3.0 specification (3.1 supports regular Json Schema).
  • Supports Open AI strict mode schemas (Optional object properties are replaced with required but nullable ones).

Sponsors

If you enjoy this package, consider dropping a couple of bucks on my GitHub Sponsors page <3

<picture height="45px"> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/colinhacks/zod/assets/3084745/ac65013f-aeb4-48dd-a2ee-41040b69cbe6"> stainless </picture>
Build AI apps and workflows with Retool AI
retool.com

Usage

Basic example

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");

Expected output

{
  "$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"]
    }
  }
}

Options

Schema name

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).

Options object

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;
  • "root" resolves $refs from the root up, ie: "#/definitions/mySchema".
  • "relative" uses relative JSON pointers. See known issues!
  • "seen" reuses the output of any "seen" Zod schema. In theory it's a more performant version of "none", but in practice this behaviour can cause issues with nested schemas and has now gotten its own option.
  • "none" ignores referencing all together, creating a new schema branch even on "seen" schemas. Recursive references defaults to "any", ie {}.
Defaults to "root".
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 escaped 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 false - whether according to strict or to passthrough. Since most parsers would retain properties given that additionalProperties = false while zod strips them, the default is to strip them unless passthrough is explicitly in the schema. On the other hand, it is useful to retain all fields unless strict is explicit in the schema which is the second option for the removeAdditional
override?: callback See section

Definitions

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 },
});

Result

{
  "type": "object",
  "properties": {
    "a": {
      "$ref": "#/definitions/myRecurringSchema"
    },
    "b": {
      "$ref": "#/definitions/myRecurringSchema"
    }
  },
  "definitions": {
    "myRecurringSchema": {
      "type": "string"
    }
  }
}

Error Messages

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 });

Result

{
  "$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.

Custom Error Message Support

  • ZodString
    • regex
    • min, max
    • email, cuid, uuid, url
    • endsWith, startsWith
  • ZodNumber
    • min, max, lt, lte, gt, gte,
    • int
    • multipleOf
  • ZodSet
    • min, max
  • ZodArray
    • min, max

override option

This options takes 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.

Since undefined is a valid option to return, if you don't want to override the current item you have to return the ignoreOverride symbol exported from the index.

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
}

Known issues

  • The OpenAI target should be considered experimental for now, as some combination of options may break the compatibility.
  • When using .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.
  • JSON Schemas does not support any other key type than strings for objects. When using 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
  • Relative JSON pointers, while published alongside JSON schema draft 2020-12, is not technically a part of it. Currently, most resolvers do not handle them at all.
  • Since v3, the Object parser uses .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.

Versioning

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.

Changelog

https://github.com/StefanTerdell/zod-to-json-schema/blob/master/changelog.md

changelog

Changelog

Version Change
3.24.1 Adds OpenAI target
3.24.0 Implements new string checks (jwt, base64url, cidr ipv4/v6), matching the new Zod version
3.23.5 Module import hotfix by Enzo Monjardín. Thanks!
3.23.4 Fixes branded regex property names and a weird edgecase in arrays. Thanks to Isaiah Marc Sanchez and Mitchell Merry!
3.23.3 More tests (Thanks Brett Zamir!), removed dead code
3.23.2 Lazily loads Emoji regex to avoid incompatibility with some environments. Thanks Jacob Lee!
3.23.1 Best-effort RegEx flag support by Spappz! Some minor fixes and additions, such as the title option.
3.23.0 Adds support for base64, date, time, duration and nanoid string validations. A warm welcome and a big thanks to Colin, the creator of Zod, joining in as a contributor :)
3.22.5 Adds new z.date() parsing options and override callback
3.22.4 Adds fix for nullable references in OpenAPI mode
3.22.3 Adjust root path from "#/" to "#" according to RFC 6901
3.22.2 Adds "output" pipe strategy
3.22.1 Fixes broken imports when using some bundlers
3.22.0 Support readonly. Export both CJS and ESM. Export everything from index. Alternative map parser. Improved pattern handling and updated sources.
3.21.4 Fixes missing support for exact array length
3.21.3 Fixes issue #77 (Reference path to nullable schemas in Open-API mode)
3.21.2 Adds "integer" type Date output to support min/max checks, markdownDescription option, fixes "none" refStrategy by adding "seen" and adds an option to use "pattern" with Zods' email enum instead of "format".
3.21.1 New target (2019-09) along with improved intersection schemas, improved mutual recursion references in definitions, descriptions respected in union parser and not removed in collapsed
3.21.0 Added new string validations (ip, emoji, etc) and BigInt checks to support Zod 3.21
3.20.5 Added uniqueItems to Set and an option to disregard pipe schemas
3.20.4 Bugfixes and improved record parsing for openApi3
3.20.3 Added Cuid2 support introduced in Zod 3.20.3
3.20.2 Reintroduced conditional simplified return-type for when target is OpenAPI 3
3.20.1 Fixed inconsistent casing in imports
3.20.0 Adds support for Zod 3.20 with catch and pipe parser as well as new string validations. Refactored Ref handling; adding definitions no longer considered experimental. Main API function refactored and simplified; output type less defined but a lot easier to maintain. Doubt anyone will miss it.
<quote>Narrator: Someone did in fact miss it</quote>
3.19.4 Adds custom error message support
3.19.3 Mark definitions as experimental in the readme
3.19.2 Added definitions option
3.19.1 Strict unions fix
3.19.0 No new features added in Zod, parity bump
3.18.2 Fixes support for native enums
3.18.1 Add strictUnions options
3.18.0 Added support for branded types
3.17.2 Fix for reference paths when supplying name option string.
3.17.1 Added startsWith and endsWith string checks. Merge multiple pattern checks into allOf array.
3.17.0 Added switch case handler for new trim "check". No changes to functionality.
3.15.x - 3.16.x Skipped: Did not change the Zod API in any way relevant for this package.
3.14.1 Dependabot security updates
3.14.0 Moves Zod into peerDependencies. Supports checks for sets, NaN-type (sort of), discriminated union type and standalone optional properties (as unions with undefined)
3.12.x - 3.13.x Skipped
3.11.3 Joins unions of enums into single enum and allows enums as keys of records
3.11.2 Adds option to target Open API 3 spec (paths) instead of Json Schema 7.
3.11.1 Performance boost when using $refStrategy none and internal improvements.
3.11.0 Added description support introduced in Zod 3.11.5
3.10.x Skipped: Minor 10 did not change the Zod API
3.9.5 Type bug fix: used dev dependency types in package
3.9.4 Path bug fix and test case when using optional definitions path
3.9.3 Added option to change definition property name to $defs
3.9.2 Added option to handle transform results as any instead of relying on their input schema.
3.9.1 Refactored the way reference pointers are passed around and added options pattern to main function without braking backwards compatibility! You can now add a base path, change the reference strategy (or opt out), and still set the schema name inside the options object or outside as before.
3.9.0 Added support for multipleOf number validaion, .rest() schemas for tuples and key validation for records (only compatible with string keys due to JSON Schema limitation).
3.7.x - 3.8.x Skipped to reach functional parity with Zod versioning.
3.6.1 Realised intersection had another potential ref pathing bug. Fixed.
3.6.0 Added support for default & effects (refine). Broke out changelog.md
3.5.0 Added support for CUID string validation
3.4.3 Fixed $ref pathing for catchall and intersection. Additional tests and code structure fixes.
3.4.2 Fixed broken intersection parser (Thanks Noah2610!)
3.4.1 Fixed pathing bug for nullable items.
3.4.0 Added support for z.lazy()
3.3.0 Added support for catchall (additionalProperties schema on objects). Rebuilt object parser.
3.2.0 Added support for Map and Set as seen by their most common JSON definitions. Beware no standardized definition seem to exist and JSON.parse doesn't handle either natively. Their implementations here are based on the spread approach. Also further simplified intersection definition to just allOf.
3.1.0 String patterns finally supported! Fixed bugs include broken external type, unsafe nullable parsing, bad intersection implementation, and missing support for passthrough keys in objects.
3.0.3 Fixed array deep pathing bug (path contained array instead of items)
3.0.2 Fixed broken type usage (NonEmptyArrayDefinition was removed from Zod)
3.0.1 Fixed a typo in the readme
3.0.0 Compatible with Zod 3.2.0. Huge props to Mr Hammad Asif for his work on this.
0.6.2 Hotfix for undefined object properties. Could crash the parser when using Pick
0.6.1 Fixed bug in union pathing. $Ref was missing /anyOf
0.6.0 Moved @types/json-schema and typescript to dev dependencies. @types/json-schema is now only used for the test suites. Using strict: true in ts config.
0.5.1 First working release with all relevant Zod types present with most validations (except for string patterns due to Zod not exposing the source regexp pattern for those).
< 0.5.1 Deprecated due to broken package structure. Please be patient, I eat crayons.