Справочник7 августа 2026 г.·обновлено 7 августа 2026 г.

Supabase: база, авторизация и API без своего бэкенда

Управляемый PostgreSQL, автоматический REST поверх схемы, вход по почте и OAuth, а права режутся построчными политиками прямо в базе. Разбор механики, ловушек RLS и способов проверить, что данные действительно закрыты.

Что это решает

Любой продукт с личным кабинетом упирается в одинаковый набор задач ещё до первой строки бизнес-логики: где держать данные, как завести пользователей, кто имеет право прочитать чужую строку, откуда фронтенд возьмёт JSON. Supabase закрывает весь набор сразу — управляемый PostgreSQL, автоматический HTTP-интерфейс поверх схемы, сервис регистрации и входа, файловое хранилище, подписка на изменения строк. Прослойка, которая только перекладывает данные из базы в браузер, перестаёт быть нужной. Экономятся недели на восстановлении пароля, кнопках входа через внешних провайдеров, рукописных CRUD-эндпоинтах и разграничении доступа.

Как устроено

Фундамент — обычная база PostgreSQL, к которой можно подключиться через psql и посмотреть таблицы. Всё остальное навешано сверху отдельными сервисами:

  • PostgREST читает схему базы и отдаёт HTTP-интерфейс. Таблица orders превращается в /rest/v1/orders, фильтры пишутся в query-строке (?status=eq.paid&select=id,total), функция в базе вызывается через /rest/v1/rpc/имя_функции. Новая колонка появляется в API сразу после миграции.
  • Сервис аутентификации держит пользователей в схеме auth и выдаёт JWT. Умеет пароль, ссылку на почту, одноразовый код, вход через внешних провайдеров.
  • Realtime слушает журнал репликации Postgres и толкает изменения строк в WebSocket. Там же broadcast-каналы и presence для «кто сейчас на странице».
  • Storage хранит файлы, а права на них описываются такими же SQL-политиками, как права на строки таблиц.
  • Edge Functions — серверный код на Deno для того, что нельзя доверить браузеру: приём вебхука от платёжной системы, поход во внешний API с секретным ключом, отправка письма.
  • Studio и CLI — веб-интерфейс к базе и локальный стек в Docker для разработки.

Путь запроса выглядит так. Браузер подставляет два заголовка: публичный ключ проекта (anon) и Authorization: Bearer <JWT пользователя>. PostgREST разбирает токен, переключает подключение на роль authenticated, кладёт содержимое токена в настройки сессии и переводит query-строку в SQL. Дальше в дело вступает Row Level Security — построчные политики внутри самой базы:

alter table orders enable row level security;

create policy "свои заказы видно"
  on orders for select
  using (auth.uid() = user_id);

create policy "свои заказы можно менять"
  on orders for update
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

auth.uid() достаёт идентификатор пользователя из токена, auth.jwt() — всё остальное содержимое, включая роль и произвольные метаданные. using управляет тем, какие строки видны операции, with check — какие строки разрешено записать. Пока RLS у таблицы выключен, её содержимое доступно наружу любому, кто взял публичный ключ из исходников фронтенда. Включён без политик — таблица отвечает пустотой. Третьего состояния нет.

Проверка доступа живёт в базе, а не в коде приложения — новая таблица без политики открыта всему интернету.

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

Схема разворачивается миграциями. supabase init создаёт структуру проекта, supabase start поднимает весь стек локально в контейнерах, supabase db diff собирает SQL-файл из накопленных изменений, supabase db push накатывает его на облачный проект. Расширения Postgres ставятся обычным create extension — pgvector для эмбеддингов, pg_cron для расписаний, PostGIS для геометрии.

Полезные сценарии

  • MVP с личным кабинетом: регистрация, вход через почту и внешнего провайдера, таблицы пользователя, доступ по RLS — без единого серверного маршрута.
  • Внутренняя админка или дашборд для команды: видимость режется политиками по роли из токена, интерфейс собирается на любом фронтенд-фреймворке.
  • Мобильное приложение: клиентские библиотеки под основные платформы ходят в тот же REST, что и веб.
  • Живые счётчики и совместное редактирование: подписка на изменения таблицы через Realtime вместо опроса сервера по таймеру.
  • База для поиска по документам: pgvector рядом с бизнес-таблицами, эмбеддинги и метаданные в одном месте, права на документы — теми же политиками.
  • Приём вебхуков от платёжек и внешних систем: Edge Function проверяет подпись и пишет в базу под service_role.

Ограничения

RLS-политики остаются единственным барьером между публичным ключом и данными. Забытая политика на новой таблице открывает её всем, и ни один линтер об этом не предупредит. Каждую таблицу приходится закрывать руками, а набор политик — тестировать так же, как код.

Политики выполняются на каждой строке выборки. Подзапрос в using, который ходит в другую таблицу, легко превращает обычный select в тяжёлую операцию. Лечится выносом проверки в функцию с security definer и индексом под неё, но помнить об этом придётся постоянно.

Сложная бизнес-логика в браузер не помещается. Расчёт цены, статусные переходы, всё, что нельзя показывать клиенту, уезжает в функции базы или в Edge Functions — и код проекта расползается по трём средам: SQL, Deno, фронтенд. Отладка такого расползания стоит дороже, чем один монолитный сервер.

Встроенный пулер соединений в транзакционном режиме плохо дружит с подготовленными выражениями и сессионными настройками. ORM, рассчитанный на постоянное подключение, ведёт себя непредсказуемо; для таких клиентов нужен отдельный режим пулера.

Realtime на журнале репликации обходится недёшево: массовое обновление таблицы порождает поток событий, а фильтрация происходит после доставки. Хранилище файлов не даёт трансформаций уровня специализированных CDN, кроме базового ресайза изображений.

Бесплатный тариф приостанавливает проекты без активности, а квоты на объём базы и трафик упираются в потолок раньше, чем кажется по демо. Самостоятельный хостинг возможен: это docker-compose на десяток сервисов, который придётся обновлять, мониторить и бэкапить своими силами.

Переезд с Supabase дешевле, чем с закрытых платформ, — данные остаются в Postgres, дамп переносится куда угодно. Но авторизация, политики и Edge Functions завязаны на конкретную реализацию и переписываются заново.

Как проверить результат

Первым делом — попытаться украсть данные собственным публичным ключом. Взять anon из фронтенда и без всякого JWT постучаться в каждую таблицу:

curl "https://<проект>.supabase.co/rest/v1/orders?select=*" \
  -H "apikey: <anon>"

Пустой массив — политика работает. Строки в ответе — таблица открыта. Затем то же самое с токеном тестового пользователя: заводятся два аккаунта, у каждого своя запись, каждый должен видеть ровно одну.

Список таблиц без RLS выводится запросом к системному каталогу:

select relname
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind = 'r'
  and not c.relrowsecurity;

Результат должен быть пустым. Этот запрос стоит повесить на проверку перед каждой выкладкой — он ловит забытую таблицу раньше, чем её найдут снаружи.

Дальше производительность политик: explain analyze на тяжёлой выборке от имени роли authenticated покажет, во что развернулось условие. Последовательное сканирование вместо индексного означает, что выражение в using неиндексируемо и на росте таблицы упрётся в таймаут.

Секреты проверяются поиском по собранному бандлу: строка service_role не должна встречаться ни в одном файле, уезжающем в браузер. Вход проверяется полным проходом на реальном почтовом ящике — регистрация, письмо, подтверждение, выход, восстановление пароля, повторный вход. Встроенный отправитель писем годится только для разработки, в проде подключается свой SMTP, и это отдельный пункт чек-листа перед запуском.

Локальный стек (supabase start) обязан подниматься с нуля и накатывать все миграции на чистую базу без единой ошибки. Как только это перестаёт работать, схема в облаке разъехалась с миграциями в репозитории, и ближайшая выкладка обнаружит расхождение уже на проде.

  • supabase
  • postgresql
  • бэкенд
  • авторизация