Supabase панель администратора на React - связка, которую я в этом году собирал для трёх разных проектов: интернет-магазина, внутренней CRM логистической компании и сервиса бронирования. Задача каждый раз одна и та же - дать менеджерам рабочий интерфейс для заказов, товаров и пользователей, не поднимая отдельный бэкенд с нуля. Supabase закрывает базу данных на Postgres, аутентификацию, права доступа и хранилище файлов, а React берёт на себя интерфейс. На выходе - рабочая панель за 2-3 недели вместо месяца на классическом стеке вроде Laravel или Django.
Зачем Supabase для админ-панели, а не самописный бэкенд
Supabase поднимает Postgres и сразу генерирует REST и GraphQL API поверх таблиц - не нужно писать контроллеры для базовых операций чтения и записи. Аутентификация идёт в комплекте: email и пароль, магическая ссылка, OAuth через Google или GitHub. Для внутренней панели обычно хватает почты и пароля с приглашением по ссылке.
Row Level Security - главное, за что я выбираю Supabase для админок. Правила доступа описываются на уровне строк таблицы, а не размазываются по коду бэкенда: менеджер видит только заказы своего региона, оператор склада - только товары своей категории, и это настраивается в базе, а не в трёх местах фронтенда.
Realtime-подписки закрывают ещё одну задачу - обновление статусов на лету, без ручного рефреша страницы. Для панели заказов это ощущается как отдельная фича, хотя по факту - одна строчка кода.
Если в проекте сложная бизнес-логика - многошаговые согласования, интеграция с эквайрингом вроде Т‑Банка с обработкой вебхуков и возвратов, синхронизация с 1С - Supabase такое тянет с оговорками. Для таких случаев я обычно закладываю отдельный бэкенд на Laravel (от 100 000 ₽), а Supabase оставляю только под данные и авторизацию.
Настройка проекта Supabase: таблицы, RLS и ключи API
Создаю проект на supabase.com, выбираю регион ближе к пользователям панели - для российских проектов обычно Frankfurt или Singapore, задержка ощутимо ниже, чем на американских дата-центрах.
В Table Editor завожу таблицы под сущности панели: orders, products, profiles. Для каждой сразу включаю Row Level Security - по умолчанию Supabase создаёт таблицу с выключенным RLS, и это ловушка, в которую попадают чаще, чем кажется. Видел проект, где таблица с email и телефонами клиентов неделю висела в открытом доступе именно из-за этого - RLS просто забыли включить после создания таблицы.
Дальше - ключи. В настройках проекта их два: anon (публичный, безопасен для фронтенда) и service_role (полный доступ в обход RLS). Service_role используется только на сервере - в Edge Functions или на своём бэкенде, никогда не в React-коде, даже если «это же внутренняя панель для своих». Утечка такого ключа через собранный бандл открывает всю базу целиком.
Для каждой таблицы сразу пишу политики доступа: кто читает, кто пишет, при каком условии. Без политик при включённом RLS таблица просто недоступна никому - это защита по умолчанию, а не баг конфигурации.
Подключение Supabase-клиента к React-проекту
Ставлю официальный клиент и создаю файл с инициализацией - обычно src/lib/supabase.js. Ключи храню в переменных окружения, а не в коде, чтобы развести dev и prod проекты Supabase без правки исходников.
npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
)
export default supabase
Для входа в панель использую signInWithPassword - простой email и пароль подходят для внутреннего инструмента лучше, чем OAuth: не нужно объяснять бухгалтеру, что такое вход через Google. Сессию Supabase хранит в localStorage и обновляет токен сама, поэтому на фронтенде остаётся только проверка - есть активная сессия или нет - и редирект на страницу логина при её отсутствии.
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
setError('Неверный логин или пароль')
}
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
CRUD-операции и realtime-обновления в панели
Базовые операции - чтение списка, обновление статуса, удаление записи - ложатся в несколько строк без отдельного API-слоя. Для панели заказов беру список с сортировкой по дате, для карточки - один запрос по id.
Обновление статуса заказа менеджером - самый частый сценарий. В паре проектов на update я вешаю realtime-подписку, которая через n8n (автоматизация от 25 000 ₽) отправляет вебхук в Telegram-бота на aiogram, и ответственный сотрудник получает уведомление о смене статуса без захода в панель. Похожая схема стояла у меня для интеграции с трекингом СДЭК - как только заказ переходил в статус «передан в СДЭК», бот в Telegram присылал трек-номер клиенту.
const { data: orders } = await supabase
.from('orders')
.select('id, status, total, created_at')
.order('created_at', { ascending: false })
await supabase
.from('orders')
.update({ status: 'shipped' })
.eq('id', orderId)
supabase
.channel('orders-changes')
.on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'orders' }, (payload) => {
console.log('Заказ обновлён', payload.new)
})
.subscribe()
Роли и права доступа в Supabase панели
Для панели с несколькими ролями - админ, менеджер, оператор склада - завожу таблицу profiles с полем role, привязанную к auth.users по id. Политики RLS дальше проверяют роль текущего пользователя через вложенный запрос к profiles или через кастомный JWT claim, если ролей много и подзапрос в каждой политике начинает бить по производительности.
Пример условия в политике на обновление заказов - доступ разрешён, только если у текущего пользователя в profiles стоит role = 'admin' или role = 'manager'. Оператор склада с ролью warehouse такую строку изменить не может, даже если знает id записи и дёргает API напрямую в обход интерфейса.
Проверяю права всегда с чужого аккаунта, а не только по коду фронтенда - RLS должен резать доступ на уровне базы. Если политика неправильная, а фронт просто «не показывает» кнопку удаления, это не защита, а фасад, который обходится одним запросом через devtools.
Supabase против альтернатив: что выбрать для админ-панели
Перед стартом обычно сравниваю четыре варианта - вот как они выглядят на практике по срокам и гибкости.
| Вариант | Запуск | Гибкость ролей | Когда выбрать |
|---|---|---|---|
| Supabase | 2-3 недели | RLS из коробки, свой сервер не нужен | Стандартный CRUD, MVP, внутренние панели |
| Firebase | 2-3 недели | Security Rules, менее гибко для сложных ролей | Мобильные проекты, простая логика |
| Свой бэкенд на Laravel | от 4-6 недель | Полный контроль над логикой | Сложная бизнес-логика, интеграции с 1С и эквайрингом |
| No-code (Tilda + скрипты) | 1-2 недели | Ограниченная | Простая витрина без сложной админки |
Если логика панели выходит за рамки CRUD - нужны отчёты с агрегацией по нескольким источникам, интеграция с 1С или сложные роли с делегированием - соберу CRM или админ-панель на React под ключ, с архитектурой под конкретные процессы, а не подгонкой под ограничения одного сервиса.
Кастомный CRM/админ-интерфейс
Админка / CRM
от 100 000 ₽
Подробнее →Частые вопросы
Сколько стоит подключить Supabase к готовой React-панели?
Зависит от объёма таблиц и ролей. Если каркас панели уже есть и нужно только развернуть базу, RLS и авторизацию - такая доработка укладывается в стоимость кастомного скрипта от 3 000 ₽. Полноценная CRM или админ-панель на React с нуля, с ролями, CRUD-интерфейсами и интеграциями - от 100 000 ₽. На консультации (от 3 000 ₽) обычно за час прикидываю точный объём под конкретный проект.
Нужен ли отдельный бэкенд, если использую Supabase?
Для большинства внутренних панелей - нет, автосгенерированного API и Edge Functions хватает. Отдельный бэкенд на Laravel (от 100 000 ₽) добавляю, когда бизнес-логика тяжелее CRUD: сложные расчёты, синхронизация с внешними системами вроде 1С или банковского эквайринга, где нужен полный контроль над транзакциями.
Как защитить админ-панель от доступа обычных пользователей?
Основная защита - RLS-политики на уровне базы, а не проверки в React-компонентах. Роль пользователя храню в отдельной таблице profiles и проверяю в каждой политике на select, insert, update и delete. Дополнительно закрываю саму страницу входа по домену или IP на уровне хостинга, если панель не должна быть публично доступна вообще.
Можно ли перенести данные с Supabase на свой сервер позже?
Да, под капотом обычный Postgres, экспорт делается стандартным pg_dump без привязки к проприетарному формату. Переносил так один проект на выделенный сервер, когда команда выросла и потребовался больше контроля над бэкапами - заняло около дня вместе с переносом Edge Functions на отдельный воркер.