Converts Zod schemas to JSON Schema format with support for multiple targets and extensive configuration options
The zod-to-json-schema library provides powerful advanced features for handling complex use cases, including reference resolution, custom parsing logic, post-processing, and error handling capabilities.
The library includes a sophisticated reference resolution system for handling circular references and reused schemas.
type RefStrategy = "root" | "relative" | "none" | "seen";Uses absolute paths from the document root:
zodToJsonSchema(schema, {
$refStrategy: "root",
basePath: ["#"]
})
// Produces: { "$ref": "#/definitions/MySchema" }Uses relative paths between schemas:
zodToJsonSchema(schema, {
$refStrategy: "relative"
})
// Produces: { "$ref": "1/definitions/MySchema" }Inlines all schemas without using $ref:
zodToJsonSchema(schema, {
$refStrategy: "none"
})
// Produces fully inlined schemasConverts previously seen schemas to any type to break cycles:
zodToJsonSchema(schema, {
$refStrategy: "seen"
})
// Circular references become: {}interface Refs {
seen: Map<ZodTypeDef, Seen>;
currentPath: string[];
propertyPath: string[] | undefined;
flags: { hasReferencedOpenAiAnyType: boolean };
}
interface Seen {
def: ZodTypeDef;
path: string[];
jsonSchema: JsonSchema7Type | undefined;
}The Refs object tracks all processed schemas to handle circular references and enable reuse.
function getRefs(options?: string | Partial<Options<Targets>>): Refs;
function getRelativePath(pathA: string[], pathB: string[]): string;Override callbacks allow custom parsing logic for specific schema types.
type OverrideCallback = (
def: ZodTypeDef,
refs: Refs,
seen: Seen | undefined,
forceResolution?: boolean
) => JsonSchema7Type | undefined | typeof ignoreOverride;const customOverride: OverrideCallback = (def, refs, seen, forceResolution) => {
// Custom handling for string schemas
if (def.typeName === "ZodString") {
return {
type: "string",
format: "custom-format",
pattern: "^custom-.*"
};
}
// Custom handling for number schemas with specific constraints
if (def.typeName === "ZodNumber" && def.checks) {
const hasMinMax = def.checks.some((c: any) => c.kind === "min" || c.kind === "max");
if (hasMinMax) {
return {
type: "number",
minimum: 0,
maximum: 100,
multipleOf: 0.01
};
}
}
// Use default parser for other types
return ignoreOverride;
};
const jsonSchema = zodToJsonSchema(schema, {
override: customOverride
});const ignoreOverride: unique symbol;Return this symbol from override callbacks to use the default parser.
Post-processing callbacks allow modification of generated schemas after initial conversion.
type PostProcessCallback = (
jsonSchema: JsonSchema7Type | undefined,
def: ZodTypeDef,
refs: Refs
) => JsonSchema7Type | undefined;const jsonDescription: PostProcessCallback;Parses JSON from Zod schema descriptions to embed additional JSON Schema properties:
const schema = z.string().describe('{"format": "email", "examples": ["user@example.com"]}');
const jsonSchema = zodToJsonSchema(schema, {
postProcess: jsonDescription
});
// Results in: { type: "string", format: "email", examples: ["user@example.com"] }const addExamples: PostProcessCallback = (jsonSchema, def, refs) => {
if (!jsonSchema) return jsonSchema;
// Add examples based on schema type
if (jsonSchema.type === "string") {
return {
...jsonSchema,
examples: ["example-string"]
};
}
if (jsonSchema.type === "number") {
return {
...jsonSchema,
examples: [42]
};
}
return jsonSchema;
};
const jsonSchema = zodToJsonSchema(schema, {
postProcess: addExamples
});The library supports custom validation error messages through the error message system.
type ErrorMessages<
T extends JsonSchema7TypeUnion | { format: string } | { pattern: string },
OmitProperties extends string = ""
> = Partial<
Omit<{ [key in keyof T]: string }, OmitProperties | "type" | "errorMessages">
>;function addErrorMessage<T extends { errorMessage?: ErrorMessages<any> }>(
res: T,
key: keyof T,
errorMessage: string | undefined,
refs: Refs
): void;
function setResponseValueAndErrors<
Json7Type extends JsonSchema7TypeUnion & { errorMessage?: ErrorMessages<Json7Type> },
Key extends keyof Omit<Json7Type, "errorMessage">
>(
res: Json7Type,
key: Key,
value: Json7Type[Key],
errorMessage: string | undefined,
refs: Refs
): void;const schema = z.string()
.min(5, "String must be at least 5 characters")
.max(20, "String must be at most 20 characters")
.email("Must be a valid email address");
const jsonSchema = zodToJsonSchema(schema, {
errorMessages: true
});
// Results in schema with errorMessage propertiesfunction parseDef(
def: ZodTypeDef,
refs: Refs,
forceResolution?: boolean
): JsonSchema7Type | undefined;Core function that parses Zod type definitions into JSON Schema.
Parameters:
def: The Zod type definition to parserefs: Reference tracking objectforceResolution: Forces new schema generation even if seen beforefunction selectParser(
def: any,
typeName: ZodFirstPartyTypeKind,
refs: Refs
): JsonSchema7Type | undefined | InnerDefGetter;
type InnerDefGetter = () => any;Routes to appropriate parser based on Zod schema type. May return a function for lazy evaluation.
When using OpenAI target format, the library automatically handles the special any type:
const schema = z.any();
const jsonSchema = zodToJsonSchema(schema, {
target: "openAi",
openAiAnyTypeName: "CustomAnyType"
});
// Generates recursive any type definition compatible with OpenAIThe library warns when using union types at the root level with OpenAI target:
const unionSchema = z.union([z.string(), z.number()]);
const jsonSchema = zodToJsonSchema(unionSchema, {
target: "openAi"
});
// Console warning: "OpenAI may not support schemas with unions as roots!"The library uses lazy evaluation for recursive schemas to optimize performance:
// Lazy evaluation for ZodLazy schemas
case ZodFirstPartyTypeKind.ZodLazy:
return () => (def as any).getter()._def;The seen map provides automatic memoization of processed schemas:
const refs = getRefs(options);
// refs.seen automatically caches processed schemasconst schema = z.object({
name: z.string(),
age: z.number()
});
const targets: Targets[] = ["jsonSchema7", "openApi3", "openAi"];
const schemas = targets.map(target => ({
target,
schema: zodToJsonSchema(schema, { target })
}));const baseSchema = z.object({
id: z.string(),
name: z.string()
});
const extendedSchema = z.object({
base: baseSchema,
items: z.array(baseSchema),
optional: baseSchema.optional()
});
const jsonSchema = zodToJsonSchema(extendedSchema, {
name: "Extended",
$refStrategy: "root",
definitions: {
Base: baseSchema
}
});Install with Tessl CLI
npx tessl i tessl/npm-zod-to-json-schema