Таблицы и REST
GET|HEAD|POST|PATCH|DELETE https://data.layero.ru/<база>/rest/v1/<таблица>
Форма запроса — PostgREST, поэтому фильтры, сортировка, встроенные ресурсы и
заголовки Prefer работают так же, как в документации Supabase.
# первые пять записей, новые сверху
curl "https://data.layero.ru/moya-baza/rest/v1/entries?select=id,name,created_at&order=created_at.desc&limit=5" \
-H "apikey: pk_live_…"
# одна запись по условию
curl "https://data.layero.ru/moya-baza/rest/v1/entries?id=eq.42&select=*" \
-H "apikey: pk_live_…"
Если limit не задан, шлюз ставит свой — 1000 строк. Запрос без предела
выедал бы пул соединений, общий для всех баз платформы.
Доступ: грант плюс политика
Свежая база не отдаёт по API ни одной таблицы. Чтобы таблица стала видна, роли нужен грант, а таблице — политика RLS:
GRANT SELECT ON public.entries TO layero.role('anon');
ALTER TABLE public.entries ENABLE ROW LEVEL SECURITY;
CREATE POLICY entries_read_own ON public.entries
FOR SELECT TO PUBLIC
USING (owner_id = auth.uid());
🚨 Имя роли берите из layero.role(), а не из pg_roles. Имя содержит
идентификатор базы, вписать его буквально нельзя, и соблазн найти по шаблону
(rolname ~ '^u_.*_anon$') выглядит естественным. Он неверен: список ролей
общий на весь сервер, шаблон совпадает с ролями чужих арендаторов, и права
уезжают не туда. Функция layero.role('anon'|'auth'|'svc') есть в каждой базе
с включённым API и возвращает вашу роль.
Проверьте выданное не на глаз: панель выполнит запрос от роли ключа — от
anon, от вошедшего пользователя или от секретного ключа — и откатит
транзакцию.
Отказы, которые вы увидите
| Ответ | Что это значит |
|---|---|
unknown_table | таблицы нет в каталоге роли, под которой открыт запрос — имя этой роли названо в тексте отказа. Почти всегда это грант, ушедший не той роли |
no_table_grant | таблица есть, но у роли нет нужного права; в hint лежит готовый GRANT |
permission_denied / 42501 | отказ самой базы |
| пустой массив | грант есть, а политика RLS не пускает ни одной строки |
Разница между последними двумя — самая частая точка непонимания: пустой ответ — не ошибка, а работающая политика.
Запись
// insert + вернуть вставленное (так по умолчанию делает supabase-js)
await db.from("entries").insert({ name: "Аня" }).select();
Prefer: return=representation возвращает строку — и попадает под
политику чтения, а не только вставки. Если политика чтения не пускает автора,
вставка пройдёт, а запрос ответит отказом RLS; подробности и рецепт —
Грабли переезда.