Definir esquemas
Para validar datos, primero debes definir un esquema. Los esquemas representan tipos, desde valores primitivos simples hasta objetos anidados complejos y arrays.
Primitivos
Coerción
Para forzar la conversión de los datos de entrada al tipo apropiado, usa z.coerce en su lugar:
La variante de coerción de estos esquemas intenta convertir el valor de entrada al tipo apropiado.
El tipo de entrada de estos esquemas de coerción es unknown por defecto. Para especificar un tipo de entrada más específico, pasa un parámetro genérico:
Literales
Los esquemas literales representan un tipo literal, como "hello world" o 5.
Para representar los literales de JavaScript null y undefined:
Para permitir múltiples valores literales:
Para extraer el conjunto de valores permitidos de un esquema literal:
Cadenas de texto (Strings)
Zod proporciona varias validaciones y transformaciones de cadenas integradas. Para realizar algunas validaciones de cadenas comunes:
Para realizar algunas transformaciones de cadenas simples:
Formatos de cadena
Para validar contra algunos formatos de cadena comunes:
Correos electrónicos (Emails)
Para validar direcciones de correo electrónico:
Por defecto, Zod usa una expresión regular de correo electrónico comparativamente estricta diseñada para validar direcciones de correo electrónico normales que contienen caracteres comunes. Es aproximadamente equivalente a las reglas impuestas por Gmail. Para obtener más información sobre esta expresión regular, consulta esta publicación.
Para personalizar el comportamiento de validación de correo electrónico, puedes pasar una expresión regular personalizada al parámetro pattern.
Zod exporta varias expresiones regulares útiles que podrías usar.
UUIDs
Para validar UUIDs:
Para especificar una versión particular de UUID:
La especificación UUID RFC 9562/4122 requiere que los dos primeros bits del byte 8 sean 10. Otros identificadores tipo UUID no imponen esta restricción. Para validar cualquier identificador tipo UUID:
URLs
Para validar cualquier URL compatible con WHATWG:
Como puedes ver, esto es bastante permisivo. Internamente, esto usa el constructor new URL() para validar las entradas; este comportamiento puede diferir entre plataformas y entornos de ejecución, pero es la forma más rigurosa de validar URIs/URLs en cualquier entorno de ejecución/motor JS dado.
Para validar el nombre de host contra una expresión regular específica:
Para validar el protocolo contra una expresión regular específica, usa el parámetro protocol.
URLs Web — En muchos casos, querrás validar URLs Web específicamente. Aquí está el esquema recomendado para hacerlo:
Esto restringe el protocolo a http/https y asegura que el nombre de host es un nombre de dominio válido con la expresión regular z.regexes.domain:
Para normalizar URLs, usa la bandera normalize. Esto sobrescribirá el valor de entrada con la URL normalizada devuelta por new URL().
Fechas y horas ISO (ISO datetimes)
Como habrás notado, z.string incluye algunas validaciones relacionadas con fecha/hora. Estas validaciones se basan en expresiones regulares, por lo que no son tan estrictas como una biblioteca completa de fecha/hora. Sin embargo, son muy convenientes para validar la entrada del usuario.
El método z.iso.datetime() impone ISO 8601; por defecto, no se permiten desplazamientos (offsets) de zona horaria:
Para permitir desplazamientos de zona horaria:
Para permitir fechas y horas no cualificadas (sin zona horaria):
Para restringir la precision (precisión) de tiempo permitida. Por defecto, los segundos son opcionales y se permite una precisión de sub-segundos arbitraria.
Fechas ISO (ISO dates)
El método z.iso.date() valida cadenas en el formato YYYY-MM-DD.
Horas ISO (ISO times)
El método z.iso.time() valida cadenas en el formato HH:MM[:SS[.s+]]. Por defecto, los segundos son opcionales, así como los decimales de sub-segundos.
No se permiten desplazamientos (offsets) de ningún tipo.
Usa el parámetro precision para restringir la precisión decimal permitida.
Direcciones IP
Bloques de IP (CIDR)
Valida rangos de direcciones IP especificados con notación CIDR.
Direcciones MAC
Valida direcciones MAC estándar de 48 bits IEEE 802.
JWTs
Valida JSON Web Tokens.
Hashes
Para validar valores hash criptográficos:
Por defecto, z.hash() espera codificación hexadecimal, como es convencional. Puedes especificar una codificación diferente con el parámetro enc:
Formatos personalizados
Para definir tus propios formatos de cadena:
Este esquema producirá problemas de tipo "invalid_format", que son más descriptivos que los errores "custom" producidos por refinements o z.custom().
Plantillas literales (Template literals)
New — Introducido en [email protected].
Para definir un esquema de plantilla literal:
La API z.templateLiteral puede manejar cualquier número de literales de cadena (ej. "hello") y esquemas. Se puede pasar cualquier esquema con un tipo inferido que sea asignable a string | number | bigint | boolean | null | undefined.
Números (Numbers)
Usa z.number() para validar números. Permite cualquier número finito.
Zod implementa varias validaciones específicas para números:
Si (por alguna razón) quieres validar NaN, usa z.nan().
Enteros (Integers)
Para validar enteros:
BigInts
Para validar BigInts:
Zod incluye varias validaciones específicas para bigint.
Booleanos (Booleans)
Para validar valores booleanos:
Fechas (Dates)
Usa z.date() para validar instancias de Date.
Para personalizar el mensaje de error:
Zod proporciona varias validaciones específicas para fechas.
Enumeraciones (Enums)
Usa z.enum para validar entradas contra un conjunto fijo de valores de cadena permitidos.
Cuidado — Si declaras tu array de cadenas como una variable, Zod no podrá inferir correctamente los valores exactos de cada elemento.
Para solucionar esto, pasa siempre el array directamente a la función z.enum(), o usa as const.
Se admiten literales de objeto similares a Enum ({ [key: string]: string | number }).
También puedes pasar un enum de TypeScript declarado externamente.
Zod 4 — Esto reemplaza la API z.nativeEnum() en Zod 3.
Ten en cuenta que el uso de la palabra clave enum de TypeScript no se recomienda.
.enum
Para extraer los valores del esquema como un objeto similar a un enum:
.exclude()
Para crear un nuevo esquema de enumeración, excluyendo ciertos valores:
.extract()
Para crear un nuevo esquema de enumeración, extrayendo ciertos valores:
Stringbools
💎 New in Zod 4
En algunos casos (por ejemplo, al analizar variables de entorno), es valioso analizar ciertos valores de cadena 'tipo booleano' a un valor boolean plano. Para admitir esto, Zod 4 introduce z.stringbool():
Para personalizar los valores verdaderos (truthy) y falsos (falsy):
Por defecto, el esquema no distingue entre mayúsculas y minúsculas (case-insensitive); todas las entradas se convierten a minúsculas antes de compararlas con los valores truthy/falsy. Para que distinga entre mayúsculas y minúsculas:
Opcionales (Optionals)
Para hacer que un esquema sea opcional (es decir, para permitir entradas undefined).
Esto devuelve una instancia de ZodOptional que envuelve el esquema original. Para extraer el esquema interno:
Anulables (Nullables)
Para hacer que un esquema sea anulable (es decir, para permitir entradas null).
Esto devuelve una instancia de ZodNullable que envuelve el esquema original. Para extraer el esquema interno:
Nullish
Para hacer que un esquema sea nullish (tanto opcional como anulable):
Consulta el manual de TypeScript para obtener más información sobre el concepto de nullish.
Unknown
Zod tiene como objetivo reflejar el sistema de tipos de TypeScript uno a uno. Como tal, Zod proporciona APIs para representar los siguientes tipos especiales:
Never
Ningún valor pasará la validación.
Objetos (Objects)
Para definir un tipo de objeto:
Por defecto, todas las propiedades son obligatorias. Para hacer que ciertas propiedades sean opcionales:
Por defecto, las claves no reconocidas se eliminan del resultado analizado:
z.strictObject
Para definir un esquema estricto que lanza un error cuando se encuentran claves desconocidas:
z.looseObject
Para definir un esquema laxo (loose) que permite que pasen claves desconocidas:
.catchall()
Para definir un esquema catchall que se usará para validar cualquier clave no reconocida:
.shape
Para acceder a los esquemas internos:
.keyof()
Para crear un esquema ZodEnum a partir de las claves de un esquema de objeto:
.extend()
Para agregar campos adicionales a un esquema de objeto:
¡Esta API puede usarse para sobrescribir campos existentes! ¡Ten cuidado con este poder! Si los dos esquemas comparten claves, B anulará a A.
Alternativa: sintaxis de propagación (spread) — Alternativamente, puedes evitar .extend() por completo creando un nuevo esquema de objeto completamente. Esto hace que el nivel de rigor del esquema resultante sea visualmente obvio.
También puedes usar esto para fusionar varios objetos de una sola vez.
Este enfoque tiene algunas ventajas:
- Usa características a nivel de lenguaje (sintaxis de propagación (spread)) en lugar de APIs específicas de la biblioteca
- La misma sintaxis funciona en Zod y Zod Mini
- Es más eficiente para
tsc: el método.extend()puede ser costoso en esquemas grandes y debido a una limitación de TypeScript se vuelve cuadráticamente más costoso cuando las llamadas se encadenan - Si lo deseas, puedes cambiar el nivel de rigor del esquema resultante usando
z.strictObject()oz.looseObject()
.safeExtend()
El método .safeExtend() funciona de manera similar a .extend(), pero no le permitirá sobrescribir una propiedad existente con un esquema no asignable. En otras palabras, el resultado de .safeExtend() tendrá un tipo inferido que extiende el original (en el sentido de TypeScript).
Usa .safeExtend() para extender esquemas que contienen refinamientos. (El .extend() normal lanzará un error cuando se use en esquemas con refinamientos).
.pick()
Inspirado por los tipos de utilidad Pick y Omit integrados en TypeScript, Zod proporciona APIs dedicadas para seleccionar (pick) y omitir (omit) ciertas claves de un esquema de objeto.
Partiendo de este esquema inicial:
Para seleccionar ciertas claves:
.omit()
Para omitir ciertas claves:
.partial()
Por conveniencia, Zod proporciona una API dedicada para hacer que algunas o todas las propiedades sean opcionales, inspirada en el tipo de utilidad incorporado de TypeScript Partial.
Para hacer que todos los campos sean opcionales:
Para hacer que ciertas propiedades sean opcionales:
.required()
Zod proporciona una API para hacer que algunas o todas las propiedades sean obligatorias, inspirada en el tipo de utilidad de TypeScript Required.
Para hacer que todas las propiedades sean obligatorias:
Para hacer que ciertas propiedades sean obligatorias:
Objetos recursivos (Recursive objects)
Para definir un tipo autorreferencial, usa un getter en la clave. Esto permite que JavaScript resuelva el esquema cíclico en tiempo de ejecución.
Aunque se admiten esquemas recursivos, pasar datos cíclicos a Zod causará un bucle infinito.
También puedes representar tipos mutuamente recursivos:
Todas las APIs de objetos (.pick(), .omit(), .required(), .partial(), etc.) funcionan como esperarías.
Errores de circularidad
Debido a las limitaciones de TypeScript, la inferencia de tipos recursivos puede ser delicada y solo funciona en ciertos escenarios. Algunos tipos más complicados pueden desencadenar errores de tipo recursivo como este:
En estos casos, puedes resolver el error con una anotación de tipo en el getter problemático:
Arrays
Para definir un esquema de array:
Para acceder al esquema interno de un elemento del array.
Zod implementa varias validaciones específicas para arrays:
Tuplas (Tuples)
A diferencia de los arrays, las tuplas suelen ser arrays de longitud fija que especifican diferentes esquemas para cada índice.
Para agregar un argumento variádico ("rest"):
Uniones (Unions)
Los tipos de unión (A | B) representan un "O" (OR) lógico. Los esquemas de unión de Zod comprobarán la entrada con cada opción en orden. Se devuelve el primer valor que se valida con éxito.
Para extraer los esquemas de opciones internos:
Uniones discriminadas (Discriminated unions)
Una unión discriminada es un tipo especial de unión en la que a) todas las opciones son esquemas de objeto que b) comparten una clave particular (el "discriminador"). Basándose en el valor de la clave discriminadora, TypeScript es capaz de "estrechar" (narrow) la firma de tipo como esperarías.
Podrías representarlo con un z.union() regular. Pero las uniones regulares son ingenuas: verifican la entrada contra cada opción en orden y devuelven la primera que pasa. Esto puede ser lento para uniones grandes.
Así que Zod proporciona una API z.discriminatedUnion() que usa una clave discriminadora para hacer el análisis más eficiente.
Cada opción debe ser un esquema de objeto cuya propiedad discriminadora (status en el ejemplo anterior) corresponde a algún valor literal o conjunto de valores, generalmente z.enum(), z.literal(), z.null(), o z.undefined().
Intersecciones (Intersections)
Los tipos de intersección (A & B) representan un "Y" (AND) lógico.
Esto puede ser útil para intersectar dos tipos de objetos.
Al fusionar esquemas de objetos, prefiere A.extend(B) sobre las intersecciones. Usar .extend() te dará un nuevo esquema de objeto, mientras que z.intersection(A, B) devuelve una instancia ZodIntersection que carece de métodos de objeto comunes como pick y omit.
Registros (Records)
Los esquemas de registro se utilizan para validar tipos como Record<string, string>.
El esquema de clave puede ser cualquier esquema de Zod que sea asignable a string | number | symbol.
Para crear esquemas de objetos que contengan claves definidas por un enum:
Zod 4 — En Zod 4, si pasas un z.enum como primer argumento a z.record(), Zod verificará exhaustivamente que todos los valores del enum existan en la entrada como claves. Este comportamiento concuerda con TypeScript:
En Zod 3, no se verificaba la exhaustividad. Para replicar el comportamiento anterior, usa z.partialRecord().
Si deseas un tipo de registro parcial, usa z.partialRecord(). Esto omite las verificaciones especiales de exhaustividad que Zod ejecuta normalmente con esquemas de clave z.enum() y z.literal().
Mapas (Maps)
Conjuntos (Sets)
Los esquemas de conjuntos se pueden restringir aún más con los siguientes métodos de utilidad.
Archivos (Files)
Para validar instancias de File:
Promesas (Promises)
Obsoleto — z.promise() está obsoleto en Zod 4. Hay muy pocos casos de uso válidos para un esquema Promise. Si sospechas que un valor podría ser una Promise, simplemente usa await antes de analizarlo con Zod.
Instancia de (Instanceof)
Puedes usar z.instanceof para verificar que la entrada es una instancia de una clase. Esto es útil para validar entradas contra clases que se exportan desde bibliotecas de terceros.
Propiedad (Property)
Para validar una propiedad particular de una instancia de clase contra un esquema Zod:
The z.property() API works with any data type (but it's most useful when used in conjunction with z.instanceof()).
Refinamientos (Refinements)
Cada esquema Zod almacena un array de refinamientos. Los refinamientos son una forma de realizar una validación personalizada para la que Zod no proporciona una API nativa.
.refine()
Las funciones de refinamiento nunca deberían lanzar errores. En su lugar, deben devolver un valor falso (falsy) para indicar un error. Los errores lanzados no son capturados por Zod.
error
Para personalizar el mensaje de error:
abort
Por defecto, los problemas de validación de las comprobaciones se consideran continuables; es decir, Zod ejecutará todas las comprobaciones en secuencia, incluso si una de ellas causa un error de validación. Esto suele ser deseable, ya que significa que Zod puede mostrar tantos errores como sea posible de una sola vez.
Para marcar un refinamiento particular como no continuable, usa el parámetro abort. La validación terminará si la comprobación falla.
path
Para personalizar la ruta del error, usa el parámetro path. Esto suele ser útil solo en el contexto de esquemas de objetos.
Esto establecerá el parámetro path en el problema asociado:
Para definir un refinamiento asíncrono, simplemente pasa una función async:
Si usas refinamientos asíncronos, ¡debes usar el método .parseAsync para analizar los datos! De lo contrario, Zod lanzará un error.
when
Nota — Esta es una característica para usuarios avanzados y puede ser abusada de maneras que aumentarán la probabilidad de errores no detectados que se originan dentro de tus refinamientos.
Por defecto, los refinamientos no se ejecutan si ya se han encontrado problemas no continuables. Zod tiene cuidado de asegurar que la firma de tipo del valor sea correcta antes de pasarlo a cualquier función de refinamiento.
En algunos casos, deseas un control más preciso sobre cuándo se ejecutan los refinamientos. Por ejemplo, considera esta verificación de "confirmación de contraseña":
Un error en anotherField evitará que se ejecute la verificación de confirmación de contraseña, aunque la verificación no dependa de anotherField. Para controlar cuándo se ejecutará un refinamiento, usa el parámetro when:
.superRefine()
La API normal .refine solo genera un único problema con un código de error "custom", pero .superRefine() hace posible crear múltiples problemas utilizando cualquiera de los tipos de problemas internos de Zod.
.check()
Nota — La API .check() es una API de más bajo nivel que generalmente es más compleja que .superRefine(). Puede ser más rápida en rutas de código sensibles al rendimiento, pero también es más verbosa.
Códecs (Codecs)
New — Introducido en Zod 4.1. Consulta la página dedicada a Códecs para obtener más información.
Los códecs son un tipo especial de esquema que implementan transformaciones bidireccionales entre otros dos esquemas.
Una operación regular .parse() realiza la transformación hacia adelante. Llama a la función decode del códec.
Alternativamente, puedes usar la función de nivel superior z.decode(). A diferencia de .parse() (que acepta una entrada unknown), z.decode() espera una entrada fuertemente tipada (string en este ejemplo).
Para realizar la transformación inversa, usa la inversa: z.encode().
Consulta la página dedicada a Códecs para obtener más información. Esa página contiene implementaciones para códecs comúnmente necesarios que puedes copiar/pegar en tu proyecto:
stringToNumberstringToIntstringToBigIntnumberToBigIntisoDatetimeToDateepochSecondsToDateepochMillisToDatejsonCodecutf8ToBytesbytesToUtf8base64ToBytesbase64urlToByteshexToBytesstringToURLstringToHttpURLuriComponentstringToBoolean
Tuberías (Pipes)
Los esquemas se pueden encadenar en "tuberías" (pipes). Las tuberías son principalmente útiles cuando se usan junto con Transformaciones.
Transformaciones (Transforms)
Nota — Para transformaciones bidireccionales, usa códecs.
Las transformaciones son un tipo especial de esquema que realizan una transformación unidireccional. En lugar de validar la entrada, aceptan cualquier cosa y realizan alguna transformación en los datos. Para definir una transformación:
Las funciones de transformación nunca deben lanzar errores. Los errores lanzados no son capturados por Zod.
Para realizar la lógica de validación dentro de una transformación, usa ctx. Para reportar un problema de validación, agrega un nuevo problema a ctx.issues (similar a la API .check()).
Más comúnmente, las transformaciones se usan junto con Tuberías. Esta combinación es útil para realizar alguna validación inicial, luego transformar los datos analizados a otra forma.
.transform()
Conectar (piping) algún esquema en una transformación es un patrón común, por lo que Zod proporciona un método de conveniencia .transform().
Las transformaciones también pueden ser asíncronas:
Si usas transformaciones asíncronas, ¡debes usar .parseAsync o .safeParseAsync al analizar datos! De lo contrario, Zod lanzará un error.
.preprocess()
Conectar (piping) una transformación en otro esquema es otro patrón común, por lo que Zod proporciona una función de conveniencia z.preprocess().
Valores por defecto (Defaults)
Para establecer un valor por defecto para un esquema:
Alternativamente, puedes pasar una función que se volverá a ejecutar cada vez que se necesite generar un valor por defecto:
Prefaults
En Zod, establecer un valor por defecto cortocircuitará el proceso de análisis. Si la entrada es undefined, el valor por defecto se devuelve ansiosamente. Como tal, el valor por defecto debe ser asignable al tipo de salida del esquema.
A veces, es útil definir un valor prefault ("valor por defecto pre-análisis"). Si la entrada es undefined, el valor prefault se analizará en su lugar. El proceso de análisis no se cortocircuita. Como tal, el valor prefault debe ser asignable al tipo de entrada del esquema.
Esto también es útil si deseas pasar algún valor de entrada a través de algunos refinamientos mutantes.
Catch
Usa .catch() para definir un valor de respaldo que se devolverá en caso de un error de validación:
Alternativamente, puedes pasar una función que se volverá a ejecutar cada vez que se necesite generar un valor de captura (catch).
Tipos de marca (Branded types)
El sistema de tipos de TypeScript es estructural, lo que significa que dos tipos que son estructuralmente equivalentes se consideran el mismo.
En algunos casos, puede ser deseable simular el tipado nominal dentro de TypeScript. Esto se puede lograr con tipos de marca (branded types) (también conocidos como "tipos opacos").
Internamente, esto funciona adjuntando una "marca" (brand) al tipo inferido del esquema.
Con esta marca, cualquier estructura de datos plana (sin marca) ya no es asignable al tipo inferido. Tienes que analizar algunos datos con el esquema para obtener datos con marca.
Ten en cuenta que los tipos de marca no afectan el resultado en tiempo de ejecución de .parse. Es una construcción solo estática.
Readonly
Para marcar un esquema como de solo lectura (readonly):
El tipo inferido de los nuevos esquemas se marcará como readonly. Ten en cuenta que en TypeScript, esto solo afecta a objetos, arrays, tuplas, Set y Map:
Las entradas se analizarán normalmente, luego el resultado se congelará con Object.freeze() para evitar modificaciones.
JSON
Para validar cualquier valor codificable en JSON:
Esta es una API de conveniencia que devuelve el siguiente esquema de unión:
Funciones (Functions)
Zod proporciona una utilidad z.function() para definir funciones validadas por Zod. De esta manera, puedes evitar mezclar el código de validación con tu lógica de negocio.
Los esquemas de función tienen un método .implement() que acepta una función y devuelve una nueva función que valida automáticamente sus entradas y salidas.
Esta función lanzará un ZodError si la entrada no es válida:
Si solo te importa validar las entradas, puedes omitir el campo output.
Usa el método .implementAsync() para crear una función asíncrona.
Personalizado (Custom)
Puedes crear un esquema Zod para cualquier tipo de TypeScript usando z.custom(). Esto es útil para crear esquemas para tipos que no son soportados por Zod de forma predeterminada, como los literales de cadena de plantilla.
Si no proporcionas una función de validación, Zod permitirá cualquier valor. ¡Esto puede ser peligroso!
Puedes personalizar el mensaje de error y otras opciones pasando un segundo argumento. Este parámetro funciona de la misma manera que el parámetro params de .refine.

