Zod — библиотека для описания и проверки структур данных во время выполнения, ориентированная на строгую типизацию и тесную интеграцию с TypeScript. Основная идея заключается в том, чтобы схема данных существовала одновременно как механизм валидации и как источник типов, автоматически выводимых для статической проверки.
JavaScript изначально не предоставляет встроенного механизма строгой проверки типов данных во время выполнения. Даже при использовании TypeScript типы существуют только на этапе компиляции и исчезают после трансляции кода. Это создаёт разрыв между тем, что ожидает программа, и тем, что реально приходит в рантайме, особенно в следующих случаях:
Любой из этих источников может содержать неожиданные структуры.
Отсутствие проверки приводит к ошибкам вида
undefined is not a function,
cannot read property of undefined и аналогичным сбоям.
Zod вводит концепцию схемы как единого источника правды:
Таким образом устраняется дублирование логики: не нужно отдельно писать интерфейсы и отдельные валидаторы.
Простейшая схема:
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
Основной механизм работы — парсинг входных данных через схему.
UserSchema.parse({
id: 1,
name: "Alex",
});
Если данные не соответствуют схеме, выбрасывается исключение.
const result = UserSchema.safeParse(input);
Возвращается объект вида:
success: true и данные при успехеsuccess: false и список ошибок при неудачеТакой подход предпочтителен для API и пользовательского ввода, где падение приложения недопустимо.
Одно из ключевых преимуществ — автоматическое получение TypeScript-типа:
type User = z.infer<typeof UserSchema>;
В результате тип User полностью соответствует схеме. Это
устраняет проблему рассинхронизации между типами и реальной
валидацией.
Библиотека предоставляет набор стандартных валидаторов:
z.string()z.number()z.boolean()z.date()z.bigint()Каждый примитив уже включает базовую проверку.
Пример:
const Age = z.number().min(0).max(120);
const Product = z.object({
title: z.string(),
price: z.number(),
});
Объекты можно вкладывать друг в друга, формируя сложные структуры.
const Tags = z.array(z.string());
const Id = z.union([z.string(), z.number()]);
Union позволяет описывать несколько допустимых форм данных.
const Schema = z.object({
description: z.string().optional(),
comment: z.string().nullable(),
});
optional() — поле может отсутствоватьnullable() — поле может быть nullЭти различия важны при работе с API, где отсутствие значения и явный null имеют разную семантику.
Zod поддерживает трансформации входных значений:
const NumberFromString = z.string().transform((val) => Number(val));
Это позволяет не только проверять данные, но и нормализовать их в нужный формат.
Можно добавлять кастомные проверки:
const Password = z.string().refine((val) => val.length >= 8, {
message: "Слишком короткий пароль",
});
Это расширяет базовые ограничения и позволяет реализовать бизнес-логику прямо в схеме.
Схемы поддерживают композицию:
const Address = z.object({
city: z.string(),
zip: z.string(),
});
const User = z.object({
name: z.string(),
address: Address,
});
Такой подход позволяет строить переиспользуемые блоки схем.
Типичный сценарий — работа с JSON:
const data = JSON.parse(raw);
const validated = UserSchema.parse(data);
Если структура нарушена, ошибка возникает на этапе парсинга, а не глубже в бизнес-логике.
По сравнению с аналогами:
Ключевое отличие заключается в том, что схема одновременно является валидатором и источником типов, а не двумя разными сущностями.
При обработке HTTP-запросов схема выступает как фильтр входных данных:
app.post("/user", (req, res) => {
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json(result.error.format());
}
const user = result.data;
});
Таким образом контролируется корректность входных данных до попадания в бизнес-логику.
Переменные окружения часто приходят как строки, даже если ожидаются числа или булевы значения:
const EnvSchema = z.object({
PORT: z.string().transform(Number),
DEBUG: z.string().transform((v) => v === "true"),
});
Это делает конфигурацию предсказуемой и безопасной.
Несмотря на гибкость, схема требует явного описания структуры. Это приводит к увеличению объёма кода в проектах с простыми типами, однако компенсируется:
Схемы можно объединять:
const BaseUser = z.object({
id: z.number(),
});
const ExtendedUser = BaseUser.extend({
role: z.string(),
});
Это позволяет строить иерархии моделей без дублирования описаний.
Поддерживается создание схем на основе параметров:
const createSchema = (minAge) =>
z.object({
age: z.number().min(minAge),
});
Такой подход используется в случаях, когда правила зависят от контекста выполнения.
Ошибки валидации содержат структурированную информацию:
Это упрощает отладку и построение пользовательских сообщений об ошибках.
Zod часто используется как слой границы между внешним миром и внутренней логикой приложения. Он выполняет функцию контракта данных:
Такой подход особенно важен в распределённых системах и при работе с микросервисами, где данные проходят через множество независимых компонентов.