Open Finapp
Справочник

Валидация

Клиентская валидация и компромиссы.

Текущий подход: только клиентская валидация

Валидация выполняется только на клиенте. Серверных валидаторов бизнес-правил нет. Сервер (Supabase Postgres) обеспечивает только типы колонок, ограничения NOT NULL, политики RLS для владельца строк и правила синхронизации по пользователям.

СлойФреймворкНазначение
Клиент (браузер)Zod v4UX форм - дефолты, трим, мгновенные ошибки; проверка формата данных на рантайме
Сервер (Supabase Postgres)Типы колонок + RLSТипы колонок, NOT NULL, владелец строки (политика RLS)

Где живёт валидация

Сущность / применениеZod-схема
Форма транзакцииapp/app/components/trns/types.ts (trnItemSchema, transactionSchema) + app/app/components/trnForm/utils/validate.ts
Конфиг статистикиapp/app/components/stat/useStatConfig.ts (ConfigSchema)
Дата / query-параметрыapp/app/components/date/statDateParams.ts (queryParamsSchema)
Настройки пользователяapp/app/components/user/types.ts (userSettingsSchema)
Формат курсов валютapp/app/components/currencies/types.ts (ratesSchema)
Локальapp/app/components/locale/types.ts (localeSchema)

Что проверяет каждая сторона

Zod (клиент):

  • .trim().min(1) - не пропускает пустые строки после трима
  • .positive() на суммах
  • .default() для начальных значений форм
  • .transform() для парсинга query-параметров (строка в число / булево)
  • Discriminated union для типа транзакции (trnItemSchema = transactionSchema | transferSchema)
  • Проверка одинаковых кошельков перевода: incomeWalletId !== expenseWalletId

Supabase Postgres (сервер):

  • Типы колонок: text, numeric, integer, boolean, jsonb
  • NOT NULL на обязательных колонках
  • RLS: каждая пользовательская таблица имеет (select auth.uid())::text = "userId" - строки других пользователей невидимы, запись отклоняется
  • Таблица rates доступна только для чтения аутентифицированным пользователям (без записи с клиента); строки пишет серверная edge-функция fetch-rates
  • Триггер автоматически создаёт user_settings при регистрации (начальная инициализация на сервере не нужна)

Серверных валидаторов бизнес-правил нет (нет серверных ограничений длины строк, диапазонов чисел, кросс-полевой валидации). Эти правила живут только в Zod-схемах на клиенте.

Компромисс

Целенаправленный клиент, обходящий форму, может записать произвольные данные в локальную SQLite, которые PowerSync загрузит в Supabase. Postgres примет их, если они проходят типы колонок и RLS. Это осознанный компромисс для однопользовательского приложения личных финансов - дублирование каждого бизнес-правила в виде Postgres-ограничений или политик RLS не оправдано по трудозатратам.

Если в будущем потребуется более строгая проверка, варианты:

  • Postgres CHECK ограничения для диапазонов и длин
  • Supabase Edge Function как прокси загрузки с серверной Zod-валидацией
  • Оценка, покрывает ли встроенная валидация Supabase (pg_jsonschema, check constraints) критические правила

Следующие шаги