Спільне використання однієї схеми Zod між вашим фронтендом на React та бекендом на Node
Дізнайтеся, як одна схема Zod може перевіряти форми React, відповіді API, тіла запитів Express та змінні середовища, водночас генеруючи відповідні типи TypeScript.
Перевірка даних має бути присутня в кожному додатку, але команди часто починають її впроваджувати поступово — одна бібліотека для фронтенду, інша — для бекенду, а ті самі правила копіюються в різних місцях. Zod став улюбленим інструментом серед розробників JavaScript та TypeScript саме через те, що уникає цього безладу: ви пишете одну схему, і ця схема водночас перевіряє ваші дані та створює відповідний тип TypeScript, готовий до використання однаково як у браузері, так і на сервері.
1. Що таке Zod?
Zod — це бібліотека схем, створена з самого початку з урахуванням TypeScript. Ви описуєте формат своїх даних один раз, і Zod використовує цей опис для перевірки значень під час виконання та для автоматичного створення типу TypeScript — немає потреби писати окремий інтерфейс, і немає ризику його неузгодженості з правилами перевірки.
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
type User = z.infer<typeof UserSchema>;
// { name: string; email: string; age?: number }
Одна така схема одночасно покриває три функції: вона документує структуру даних, забезпечує її дотримання під час виконання та надає статичний тип, від якого залежать ваш редактор та компілятор.
2. Чому Zod кращий за альтернативи
Основною перевагою є автоматичне визначення типів. Бібліотеки на кшталт Yup чи Joi зазвичай вимагають від вас підтримувати схему перевірки разом із вручну написаним інтерфейсом TypeScript, покладаючись на те, що вони не будуть розходитися під час змін кодової бази. Zod повністю усуває цей ризик: тип виводиться безпосередньо з схеми, тож немає потреби щось синхронізувати.
Zod також є легким та не має зовнішніх залежностей, що робить його однаково зручним як у пакетах фронтенду, де важлива компактність, так і у сервісах Node.js. Його можливості до створення ланцюгових та комбінованих API означають, що навіть складна валідація — з використанням вкладених об’єктів, об’єднань та полів, які залежать одне від одного — залишається зрозумілою та читабельною, а не перетворюється на заплутану масу спеціальних допоміжних функцій.
3. Використання Zod у React App
3.1 Валідація форм з React Hook Form
Zod підключається безпосередньо до React Hook Form через пакет @hookform/resolvers.
npm install zod react-hook-form @hookform/resolvers
// components/SignupForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const SignupSchema = z.object({
name: z.string().min(2, 'Name is too short'),
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type SignupData = z.infer<typeof SignupSchema>;
export function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<SignupData>({
resolver: zodResolver(SignupSchema),
});
const onSubmit = (data: SignupData) => {
console.log('Valid data:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Name" />
{errors.name && <p>{errors.name.message}</p>}
<input {...register('email')} placeholder="Email" />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register('password')} placeholder="Password" />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit">Sign Up</button>
</form>
);
}
Немає ручного відстеження стану помилок та не потрібно підтримувати дублікатів оголошень типів — одна схема водночас здійснює перевірку, надає повідомлення про помилки поруч із кожним полем та визначає тип об’єкта data, який надісланий, у форматі TypeScript.
3.2 Перевірка відповідей API
Zod також є корисним для обробки даних, які надходять у вашому додатку — наприклад, для перевірки того, чи дані, повернені з API, дійсно відповідають очікуваному формату, адже неможливо покладатися лише на типи на етапі компіляції для гарантування цього.
import { z } from 'zod';
const PostSchema = z.object({
id: z.number(),
title: z.string(),
body: z.string(),
});
const PostsResponseSchema = z.array(PostSchema);
async function fetchPosts() {
const res = await fetch('/api/posts');
const json = await res.json();
const result = PostsResponseSchema.safeParse(json);
if (!result.success) {
console.error(result.error.flatten());
throw new Error('Invalid API response shape');
}
return result.data; // fully typed Post[]
}
Цей підхід дозволяє виявляти некоректні або неочікувані відповіді ще до того, як вони спричинять приховані помилки у вашому інтерфейсі користувача.
4. Використання Zod у сервері Node.js / Express
4.1 Перевірка тіла запиту
npm install zod express
// schemas/user-schema.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
// middleware/validate.ts
import { Request, Response, NextFunction } from 'express';
import { ZodSchema } from 'zod';
export function validate(schema: ZodSchema) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
req.body = result.data;
next();
};
}
// routes/users.ts
import { Router } from 'express';
import { validate } from '../middleware/validate';
import { CreateUserSchema } from '../schemas/user-schema';
const router = Router();
router.post('/users', validate(CreateUserSchema), (req, res) => {
// req.body is now guaranteed to match CreateUserInput
const { name, email, age } = req.body;
res.status(201).json({ name, email, age });
});
export default router;
Ця конфігурація забезпечує кожному маршруту єдиний, декларативний крок перевірки з централізованим обробленням помилок замість їх повторного використання у вигляді вбудованих перевірок if у кожному обробнику.
4.2 Перевірка змінних середовища
Одним із недооцінених, але потужних способів використання Zod є перевірка process.env під час запуску додатку, щоб неправильна конфігурація призводила до негайної помилки, а не до заплутаної проблеми пізніше.
// config/env.ts
import { z } from 'zod';
const EnvSchema = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(['development', 'production', 'test']),
});
export const env = EnvSchema.parse(process.env);
Якщо бракує якоїсь необхідної змінної або вона має неправильний формат, процес негайно зупиняється із зрозумілим повідомленням про помилку — це набагато простіше для діагностики, ніж таємнича помилка, прихована в запиті до бази даних.
5. Справжня перевага: одна схема, спільна для всього стеку
/packages
/shared
/schemas
user-schema.ts <-- used by both React app and Express API
/web (React/Next.js)
/api (Node/Express)
// packages/shared/schemas/user-schema.ts
import { z } from 'zod';
export const CreateUserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().int().positive().optional(),
});
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
React-додаток використовує цю схему для перевірки форми реєстрації перед її надсиланням. API Express також використовує ту саму схему для верифікації надходящих даних. Коли схема змінюється — наприклад, з’являється нове обов’язкове поле — обидві частини системи одночасно реагують на цю зміну, а TypeScript негайно виявляє будь-який код, який ще не пристосувався до нової структури. Це усуває цілу категорію помилок, коли верифікація з боку клієнта та сервера з часом поступово відхиляється одна від одної.
6. Найкращі практики
- Використовуйте
safeParse, коли невдача є звичайним, очікуваним результатом (дані з форми, відповіді від сторонніх API), аparse, який викидає помилку, залишіть для випадків, коли дані справді ніколи не повинні бути недійсними, наприклад, змінні середовища, які перевіряються під час запуску.
.transform() для очищення даних під час самої перевірки — видалення пробілів, зміна типів — замість того, щоб пізніше виконувати окремий крок нормалізації.z.infer перед вручну написаними інтерфейсами для всього, що вже підтримується схемою, щоб ваші типи та логіка перевірки ніколи не виходили з синхронності.error.flatten() або error.format() у відповідях API на помилки, щоб код фронтенду міг легко віднести кожну помилку до відповідного поля форми.7. Висновок
Zod — це не просто звичайна бібліотека валідації; вона повністю змінює спосіб взаємодії між процесом валідації та типуванням. Генеруючи типи TypeScript безпосередньо зі схем у часі виконання, вона безшумно усуває проблему розбіжностей між визначеннями типів та правилами валідації. До цього додаються його мінімальний розмір, композитний дизайн та послідовна поведінка як у браузері, так і в Node — тому Zod є ідеальним вибором для проєктів на TypeScript з повною стек-архітектурою, побудованих на React та Node.js.
Наступні кроки:
- Ознайомтесь з
zod-to-openapi, якщо вам потрібно генерувати документацію OpenAPI безпосередньо з ваших схем - Дослідіть функції
.refine()та.superRefine()для створення власної логіки валідації, яка охоплює кілька полів - Перегляньте tRPC, який використовує схеми Zod для забезпечення повної безпеки типів у вашому API
Пов’язана література
- Zod проти express-validator: два підходи до верифікації даних у Express — порівнює верифікацію запитів на основі схеми з використанням Zod та середовища express-validator, що ґрунтується на ланцюгах обробки, розглядаючи налаштування, форматування помилок та поширені проблеми.
- Пропозиції TC39 у 2026 році: декоратори, Temporal та Signals пояснено — практичний огляд трьох пропозицій TC39 — вбудованих декораторів, API Temporal та Signals — та їхнього значення для розробників JavaScript та TypeScript у повноцінних фреймворках.