Pular para o conteúdo

Converter JSON Schema em esquema Zod — Numio

Gera código de validação Zod a partir de um JSON Schema com json-schema-to-zod. Restrições como minLength, minimum, pattern e default viram métodos Zod encadeados, e propriedades que não estão em "required" recebem .optional().

Você recebe um JSON Schema de outra equipe e quer validação em tempo de execução em um serviço TypeScript sem um validador de JSON Schema.

JSON Schema

Zod Schema

Quando usar

  • Você recebe um JSON Schema de outra equipe e quer validação em tempo de execução em um serviço TypeScript sem um validador de JSON Schema.
  • Você está migrando a validação de formulários de um validador de JSON Schema como o Ajv para o Zod e quer código gerado como ponto de partida.

Como usar

  1. Cole um JSON Schema no painel JSON Schema.
  2. Se quiser, abra as configurações e altere Root Schema Name (padrão "schema"); ele vira o nome da const exportada.
  3. Copie o código gerado, que importa z de "zod", para o seu projeto. A conversão roda no navegador.

JSON Schema

{
  "type": "object",
  "properties": {
    "username": { "type": "string", "minLength": 3, "maxLength": 20 },
    "age": { "type": "integer", "minimum": 18 },
    "contact": {
      "oneOf": [
        { "type": "string", "format": "email" },
        { "type": "string", "pattern": "^\\+[0-9]+$" }
      ]
    },
    "newsletter": { "type": "boolean", "default": false }
  },
  "required": ["username", "contact"]
}

Schema Zod

import { z } from "zod"

export const schema = z.object({
  username: z.string().min(3).max(20),
  age: z.number().int().gte(18).optional(),
  contact: z.any().superRefine((x, ctx) => {
    const schemas = [
      z.string().email(),
      z.string().regex(new RegExp("^\\+[0-9]+$")),
    ]
    const errors = schemas.reduce(
      (errors: z.ZodError[], schema) =>
        ((result) => ("error" in result ? [...errors, result.error] : errors))(
          schema.safeParse(x),
        ),
      [],
    )
    if (schemas.length - errors.length !== 1) {
      ctx.addIssue({
        path: ctx.path,
        code: "invalid_union",
        unionErrors: errors,
        message: "Invalid input: Should pass single schema",
      })
…

Detalhes e limitações

oneOf é convertido em z.any().superRefine(...), que verifica se exatamente um subschema passa, como mostra o exemplo.

$ref não é resolvido: uma propriedade referenciada vira z.any().

integer adiciona .int(), format "email" adiciona .email() e format "date-time" adiciona .datetime().

additionalProperties: false vira .catchall(z.never()) em vez de .strict().

FAQ

Como os tipos anuláveis são tratados?

"type": ["string", "null"] vira z.union([z.string(), z.null()]), com .optional() adicionado se a propriedade não for obrigatória.

Posso renomear o schema exportado?

Sim. Defina Root Schema Name nas configurações; a saída passa a ser export const <name> = ... em vez de export const schema.

definitions e $ref são suportados?

Não. Uma propriedade que usa $ref vira z.any(), então coloque o schema referenciado inline antes de converter.

Especificações e referências