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

Valibot — это современная библиотека для валидации данных, которая позволяет определять схемы проверки с типовой безопасностью и мощной поддержкой TypeScript. При работе с TanStack Router она используется для валидации параметров маршрутов, форм и загрузчиков данных. В TanStack Router маршруты могут содержать динамические сегменты, query-параметры и состояние загрузки, и именно Valibot позволяет гарантировать корректность этих данных до того, как они будут использованы в компоненте.

Определение схемы валидации

Схема в Valibot создаётся с использованием цепочек проверок. Для маршрутов TanStack Router обычно проверяются следующие элементы: params, searchParams и loaderData.

import { object, string, number } fr om 'valibot';

const userRouteSchema = object({
  params: object({
    userId: string().regex(/^\d+$/) // userId должен быть числом в виде строки
  }),
  searchParams: object({
    filter: string().optional(),
    page: number().default(1)
  })
});

В этом примере userId строго проверяется как числовая строка, filter является необязательным параметром, а page получает значение по умолчанию, если параметр не передан.

Валидация параметров маршрута

TanStack Router позволяет определять loader-функции для загрузки данных до рендеринга компонента. Внутри loader можно использовать Valibot для строгой проверки входных параметров.

import { createRoute } fr om '@tanstack/router';
import { parse } from 'valibot';

const userRoute = createRoute({
  path: '/user/:userId',
  loader: async ({ params, searchParams }) => {
    const parsed = parse(userRouteSchema, { params, searchParams });
    
    if (!parsed.success) {
      throw new Error('Некорректные параметры маршрута');
    }

    return fetch(`/api/users/${parsed.data.params.userId}?page=${parsed.data.searchParams.page}`)
      .then(res => res.json());
  }
});

Ключевой момент: использование parse обеспечивает строгую типизацию и предотвращает попадание некорректных данных в компонент. Любая ошибка валидации может быть обработана централизованно с помощью TanStack Router Error Boundaries.

Валидация форм с использованием Valibot

Valibot позволяет не только проверять параметры маршрутов, но и контролировать ввод пользователя в формах. В TanStack Router это удобно сочетать с формами, где данные отправляются через action.

import { string, object } from 'valibot';

const loginSchema = object({
  body: object({
    username: string().minLength(3),
    password: string().minLength(8)
  })
});

const loginRoute = createRoute({
  path: '/login',
  action: async ({ request }) => {
    const formData = Object.fromEntries(await request.formData());
    const result = parse(loginSchema, { body: formData });

    if (!result.success) {
      return { errors: result.errors };
    }

    return authenticateUser(result.data.body.username, result.data.body.password);
  }
});

В данном примере parse возвращает объект с детальной информацией об ошибках валидации, что позволяет отображать точные сообщения пользователю.

Обработка ошибок валидации

TanStack Router поддерживает Error Boundaries для маршрутов. Это делает возможным централизованное управление ошибками валидации, предотвращая падение всего приложения.

const userRoute = createRoute({
  path: '/user/:userId',
  loader: async ({ params }) => {
    const result = parse(userRouteSchema, { params });
    if (!result.success) throw new Error('Неверные параметры');
    return fetchUser(result.data.params.userId);
  },
  errorComponent: ({ error }) => <div>Ошибка: {error.message}</div>
});

Особенность: ошибки валидации Valibot автоматически передаются в errorComponent, позволяя разделять бизнес-логику и обработку ошибок интерфейса.

Использование дефолтных значений и трансформаций

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

import { number, object } from 'valibot';

const paginationSchema = object({
  searchParams: object({
    page: number().default(1),
    lim it: number().default(20)
  })
});

В TanStack Router loader можно безопасно использовать такие схемы:

const route = createRoute({
  path: '/items',
  loader: ({ searchParams }) => {
    const { data } = parse(paginationSchema, { searchParams });
    return fetchItems(data.searchParams.page, data.searchParams.lim it);
  }
});

Интеграция с TypeScript

Valibot предоставляет полную поддержку TypeScript, автоматически выводя типы для проверенных данных. Это особенно важно в TanStack Router, где ошибки типов на этапе компиляции предотвращают runtime-проблемы.

type UserRouteData = typeof userRouteSchema._type;

async function handleUser(data: UserRouteData) {
  console.log(data.params.userId);
  console.log(data.searchParams.page);
}

Такой подход минимизирует риск работы с некорректными данными и повышает надёжность приложения.

Рекомендации по архитектуре

  1. Выносить схемы в отдельные модули для повторного использования между loader, action и компонентами.
  2. Использовать parse вместо try/catch для удобного доступа к ошибкам валидации.
  3. Совмещать дефолтные значения и трансформации для удобного управления query-параметрами.
  4. Обрабатывать ошибки через errorComponent для единообразного UX при некорректных данных.

Valibot в связке с TanStack Router создаёт безопасную и типизированную инфраструктуру маршрутизации, позволяя контролировать все входные данные на этапе загрузки и отправки форм.