CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zod-to-json-schema

Converts Zod schemas to JSON Schema format with support for multiple targets and extensive configuration options

Overview
Eval results
Files

advanced-features.mddocs/

Advanced Features

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.

Reference Resolution System

The library includes a sophisticated reference resolution system for handling circular references and reused schemas.

Reference Strategies

type RefStrategy = "root" | "relative" | "none" | "seen";

Root References (Default)

Uses absolute paths from the document root:

zodToJsonSchema(schema, {
  $refStrategy: "root",
  basePath: ["#"]
})
// Produces: { "$ref": "#/definitions/MySchema" }

Relative References

Uses relative paths between schemas:

zodToJsonSchema(schema, {
  $refStrategy: "relative"
})
// Produces: { "$ref": "1/definitions/MySchema" }

No References

Inlines all schemas without using $ref:

zodToJsonSchema(schema, {
  $refStrategy: "none"
})
// Produces fully inlined schemas

Seen References

Converts previously seen schemas to any type to break cycles:

zodToJsonSchema(schema, {
  $refStrategy: "seen"
})
// Circular references become: {}

Reference Tracking

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.

Reference Utilities

function getRefs(options?: string | Partial<Options<Targets>>): Refs;

function getRelativePath(pathA: string[], pathB: string[]): string;

Custom Override System

Override callbacks allow custom parsing logic for specific schema types.

Override Callback

type OverrideCallback = (
  def: ZodTypeDef,
  refs: Refs,
  seen: Seen | undefined,
  forceResolution?: boolean
) => JsonSchema7Type | undefined | typeof ignoreOverride;

Usage Example

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

Ignore Override Symbol

const ignoreOverride: unique symbol;

Return this symbol from override callbacks to use the default parser.

Post-Processing System

Post-processing callbacks allow modification of generated schemas after initial conversion.

Post-Process Callback

type PostProcessCallback = (
  jsonSchema: JsonSchema7Type | undefined,
  def: ZodTypeDef,
  refs: Refs
) => JsonSchema7Type | undefined;

Built-in Post-Processor

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

Custom Post-Processing

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

Error Message System

The library supports custom validation error messages through the error message system.

Error Message Types

type ErrorMessages<
  T extends JsonSchema7TypeUnion | { format: string } | { pattern: string },
  OmitProperties extends string = ""
> = Partial<
  Omit<{ [key in keyof T]: string }, OmitProperties | "type" | "errorMessages">
>;

Error Message Functions

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;

Usage Example

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 properties

Core Processing Functions

Schema Definition Parser

function 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 parse
  • refs: Reference tracking object
  • forceResolution: Forces new schema generation even if seen before

Parser Selection

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

OpenAI Specific Features

OpenAI Any Type Handling

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 OpenAI

OpenAI Union Warnings

The 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!"

Performance Optimization

Lazy Evaluation

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;

Caching and Memoization

The seen map provides automatic memoization of processed schemas:

const refs = getRefs(options);
// refs.seen automatically caches processed schemas

Advanced Configuration Patterns

Multi-Target Conversion

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

Complex Reference Handling

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

docs

advanced-features.md

configuration.md

index.md

schema-conversion.md

types.md

tile.json