apigenie-generatorDocs and dataschema zod
schema zod
Generate Zod (v4) schemas, with their TypeScript types, from JSON Schemas.
(experimental, outside the 1.0 compatibility promise) The generated schemas aren’t yet checked against a live API (conformance Level 3), and the options and the output can still change.
schema zodsays so in its--helpand on the first line it prints, on the standard error. See the experimental commands.
Usage
apigenie-generator schema zod [...OPTIONS] INPUT_FILE
Description
The input is a JSON or YAML file:
- a JSON Schema document (drafts 4, 6, 7, 2019-09 and 2020-12, 2020-12 when it has no
$schema): its root schema, named after itstitleor the file, and its$defs(ordefinitions) are generated. A root that only holds definitions is left out; - an OpenAPI 3.x document: its
components.schemas, with the OpenAPI 3.0nullablekeyword and booleanexclusiveMinimum/exclusiveMaximum; - a Swagger 2.0 document: its
definitions, with thex-nullableextension; - an AsyncAPI document: its
components.schemas. Schemas in other formats (Avro, …) are skipped.
Other schemas that a $ref points to, e.g. #/$defs/User/properties/address or a schema of
another file, are named after their path (UserAddressSchema), so that each is written once.
Every schema becomes a constant and a type:
import * as z from "zod";
export const UserSchema = z.looseObject({
id: z.uuid(),
email: z.email(),
/** Display name */
name: z.string().min(1).max(80),
age: z.int().gte(0).lt(150).optional(),
});
export type User = z.infer<typeof UserSchema>;
Schemas are declared after the schemas they use. A schema using itself, or a schema declared later, from an object property gets a getter:
export const CommentSchema = z.strictObject({
text: z.string(),
get replies() {
return z.array(CommentSchema).optional();
},
});
Elsewhere, e.g. items of an array schema using the array itself, the reference is wrapped in
z.lazy() and the constant is typed z.ZodType, so its inferred type is unknown. This is
reported as a warning.
How keywords are mapped
| JSON Schema | Zod |
|---|---|
type |
z.string(), z.number(), z.int() (z.int32() for the int32 format), z.boolean(), z.null(), z.array(), objects; several types make a z.union(), null with other types .nullable() |
no type |
the type implied by the keywords, e.g. minLength makes a string; z.unknown() otherwise |
enum, const |
z.enum() for strings, z.literal() otherwise |
format |
z.email(), z.url() (uri, iri), z.uuid(), z.iso.datetime({ offset: true }), z.iso.date(), z.iso.duration(), z.ipv4(), z.ipv6(), z.hostname(), z.base64() (byte), z.base64url(); other formats are not validated |
minLength, maxLength, pattern |
.min(), .max(), .length(), .regex() |
minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf |
.gte(), .lte(), .gt(), .lt(), .multipleOf() |
items, minItems, maxItems |
z.array(), .min(), .max() |
prefixItems (or an array of items) |
z.tuple(), with optional items beyond minItems and a rest of items (additionalItems) |
uniqueItems, contains |
refinements |
properties, required |
the shape, with .optional() for properties that aren’t required, or .default() with their default |
additionalProperties |
none or true: z.looseObject() (unknown properties are kept); false: z.strictObject(); a schema: .catchall(), or z.record() without properties |
patternProperties |
a refinement validating the matching properties |
minProperties, maxProperties, propertyNames, dependentRequired, dependentSchemas (dependencies) |
refinements |
allOf |
z.intersection() |
anyOf, oneOf |
z.union(), or z.discriminatedUnion() when a required property with constant values tells the objects apart (e.g. the OpenAPI discriminator) |
not, if/then/else |
refinements |
$ref |
the referenced schema’s constant |
description (or title), deprecated |
JSDoc comments; .describe() with the describe option |
Not supported, and reported as warnings: unevaluatedProperties and unevaluatedItems.
$dynamicRef and $recursiveRef are resolved to their initial targets.
In very large graphs of recursive schemas, TypeScript may give up inferring a type
(TS2589: Type instantiation is excessively deep at z.infer); the schemas still validate.
Approximations: oneOf is not exclusive, uniqueItems compares items by their JSON text, string
lengths count UTF-16 code units, and uuid accepts the versions known to Zod. The types of
properties are implied by their keywords also where JSON Schema accepts other types, e.g.
{ "minLength": 1 } rejects numbers.
Extensions
| Extension | Effect |
|---|---|
x-apigenie.codegen.name |
base of the names of the constant and the type of a schema (AnimalSchema, Animal) |
x-nullable |
true makes a schema accept null, as in Swagger 2.0 documents |
The extensions can live in an overlay for this target instead of in the document, e.g.
zod.overlay.yaml:
overlay: 1.0.0
info:
title: Zod names
version: 1.0.0
actions:
- target: $.components.schemas.Pet
update:
x-apigenie:
codegen:
name: Animal
apigenie-generator schema zod openapi.yaml --overlay zod.overlay.yaml -o out
Options
-c, –config
Path to the configuration file to use. See the “Configuration” section for more information.
–overlay OVERLAY
Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those
listed under commands.generate.schema_zod.overlay in the configuration file (with paths relative
to it). Warnings and errors about a value an overlay set point to the overlay.
–strict
Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).
-o, –output
Directory where the schemas are generated.
–package-name
Name of the generated package. Defaults to the title of the document or the name of the file.
–package-version
Version of the generated package. Defaults to the version of the API, or 0.0.0.
Configuration
version: '1.0'
commands:
generate:
schema_zod:
layout: package
import_extension: .js
schema_suffix: Schema
type_suffix: ''
types: true
additional_properties: loose
describe: false
| Option | Description |
|---|---|
layout |
package (default): an npm package with src/schemas.ts and src/index.ts; sources: schemas.ts only |
import_extension |
Extension of relative imports: .js (default, Node.js) or an empty string (bundlers) |
schema_suffix |
Appended to the names of the schema constants (default Schema) |
type_suffix |
Appended to the names of the types (default none) |
types |
Whether export type X = z.infer<typeof XSchema> is generated (default true) |
additional_properties |
Objects without additionalProperties: loose (default) keeps unknown properties, strip removes them (z.object()) |
describe |
Whether descriptions are attached with .describe() (default false) |