Использование Zod для валидации

TanStack Router предоставляет гибкую систему маршрутизации для React и других JavaScript-фреймворков. Одной из ключевых задач при работе с маршрутизатором является валидация параметров маршрута и данных запроса. Для этого удобно использовать библиотеку Zod, которая позволяет создавать строгие схемы валидации и безопасно типизировать данные.


Определение схем с Zod

Zod работает на основе декларативных схем. Каждая схема описывает структуру данных и условия, которым они должны соответствовать:

import { z } from "zod";

const userSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1),
  age: z.number().int().positive()
});

В этом примере схема userSchema проверяет объект с тремя полями: id должен быть UUID, name — непустая строка, age — положительное целое число.


Интеграция с TanStack Router

TanStack Router позволяет определять маршруты с параметрами. С помощью Zod можно валидировать эти параметры до передачи их компоненту:

import { createRouter, createRoute } from "@tanstack/router";

const userRoute = createRoute({
  path: "/user/:id",
  validate: ({ params }) => {
    const result = z.object({ id: z.string().uuid() }).safeParse(params);
    if (!result.success) {
      throw new Error("Неверный параметр маршрута");
    }
    return result.data;
  },
  component: UserPage
});

const router = createRouter({
  routes: [userRoute]
});

Здесь validate используется для проверки параметров маршрута перед тем, как компонент UserPage получит доступ к данным.


Валидация query-параметров

В TanStack Router также можно валидировать query-параметры. Например, для фильтров или пагинации:

const searchRoute = createRoute({
  path: "/search",
  validate: ({ query }) => {
    return z.object({
      q: z.string().min(3),
      page: z.string().regex(/^\d+$/).transform(Number).default(1)
    }).parse(query);
  },
  component: SearchPage
});
  • q — поисковая строка, минимум 3 символа.
  • page — номер страницы, преобразуется из строки в число с дефолтным значением 1.

Валидация форм и состояния компонента

Zod хорошо работает не только с маршрутизатором, но и с формами. В связке с TanStack Router это позволяет безопасно передавать данные между страницами и компонентами:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const formSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6)
});

function LoginForm() {
  const { register, handleSubmit, formState } = useForm({
    resolver: zodResolver(formSchema)
  });

  const onSub mit = data => {
    router.navigate({ to: "/dashboard", params: { userId: data.email } });
  };

  return (
    
); }

Использование zodResolver обеспечивает строгую проверку данных формы перед навигацией, предотвращая ошибки на уровне маршрутизации.


Асинхронная валидация с Zod

Zod поддерживает асинхронные схемы, что удобно для проверки данных на сервере:

const userIdSchema = z.string().uuid().refine(async (id) => {
  const response = await fetch(`/api/users/${id}`);
  const exists = await response.json();
  return exists;
}, {
  message: "Пользователь не найден"
});

const userRoute = createRoute({
  path: "/user/:id",
  validate: async ({ params }) => {
    return await userIdSchema.parseAsync(params.id);
  },
  component: UserPage
});

Асинхронная валидация позволяет проверять параметры маршрута на сервере, прежде чем рендерить компонент, что повышает безопасность приложения.


Использование схем Zod для типизации

Zод позволяет автоматически выводить типы TypeScript из схем, что интегрируется с TanStack Router:

type User = z.infer;

const fetchUser = async (id: string): Promise => {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  return userSchema.parse(data);
};

Это обеспечивает согласованность типов на всем протяжении приложения, от API до компонентов и маршрутов.


Комбинирование схем и вложенных маршрутов

Для сложных приложений можно создавать вложенные маршруты с отдельными схемами валидации:

const postsRoute = createRoute({
  path: "/users/:userId/posts/:postId",
  validate: ({ params }) => z.object({
    userId: z.string().uuid(),
    postId: z.string().uuid()
  }).parse(params),
  component: PostPage
});

Каждый уровень маршрута может иметь собственную схему Zod, что позволяет управлять валидацией независимо и избежать дублирования кода.