Pular para o conteúdo
🔌 API REST

PostgREST automático.

API REST gerada a partir do seu schema. SELECT, INSERT, UPDATE, DELETE, filtros, joins. Sem escrever endpoint.

URL base

endpoint
https://api.superdb.com.br/{tabela}

Path raiz, sem /rest/v1/ e sem {projeto} na URL. O tenant sai da credencial que você manda — não da URL nem de um header de schema.

Autenticação

O login devolve dois tokens, e só um deles funciona aqui. Se você vem do Supabase — onde existe um token só — este é o tropeço mais caro da migração.

⚠️

Antes de tudo: o login precisa saber qual é o seu projeto. Mande X-SuperDB-Project: <slug> em toda chamada a auth.superdb.com.br — a anon key sozinha não identifica o tenant. Sem esse header a resposta é 404 tenant_not_found, que é fácil confundir com URL errada. (Se o seu projeto tem domínio próprio configurado, o host já resolve e o header é dispensável.)

resposta do POST auth.superdb.com.br/auth/v1/signin
{
  "access_token":     "v4.public.eyJzdWIiOi...",   // sessão do usuário — NÃO serve para dados
  "data_plane_token": "eyJhbGciOiJFUzI1NiI...",    // ← este vai para api.superdb.com.br
  "refresh_token":    "...",
  "expires_in":       3600
}

O access_token é a sessão (formato PASETO) e é aceito em auth.superdb.com.br. A API de dados fala JWT e só aceita o data_plane_token. Mandar o token errado devolve 401.

headers da API de dados
Authorization: Bearer $DATA_PLANE_TOKEN
💡

Não precisa mandar Accept-Profile. O tenant é deduzido da própria credencial — e um Accept-Profile divergente é ignorado em favor do projeto da chave.

Para leitura pública, a anon key vale como credencial sozinha:

curl
curl 'https://api.superdb.com.br/tasks?select=*&limit=10' \
  -H "apikey: $ANON_KEY"

O data_plane_token expira

Vale 1 hora. Renove em POST auth.superdb.com.br/auth/v1/token/refresh com o refresh_token — a resposta traz um data_plane_token novo. Se as chamadas começarem a voltar 401 depois de um tempo funcionando, é isso.

Voltou [] e você esperava dados

Consulta bloqueada por RLS devolve lista vazia, não erro — o mesmo que uma tabela sem linhas. Toda tabela nasce com RLS ligado, então uma tabela recém-criada sem política responde [] para todo mundo. É o primeiro lugar a olhar quando o corpo vem vazio sem motivo aparente.

Pedindo um registro só

O .single() do SDK (e o header Accept: application/vnd.pgrst.object+json no HTTP puro) exige exatamente uma linha. Com zero ou com duas, a resposta é 406:

406 — PGRST116
{
  "code":    "PGRST116",
  "details": "The result contains 0 rows",
  "message": "JSON object requested, multiple (or no) rows returned"
}
⚠️

Para "buscar um registro que pode ainda não existir" — o perfil do usuário logo após o cadastro, por exemplo — use .maybeSingle(). Ele devolve null em vez de erro quando não há linha.

SELECT

curl
# Todos os registros
curl https://api.superdb.com.br/tasks \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

# Filtro: tarefas não concluídas
curl "https://api.superdb.com.br/tasks?done=eq.false" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

# Selecionar colunas
curl "https://api.superdb.com.br/tasks?select=id,title" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

# Ordem + paginação
curl "https://api.superdb.com.br/tasks?order=created_at.desc&limit=10" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

Operadores de filtro

Use no formato ?coluna=op.valor. Operadores principais:

  • eq — igual: ?done=eq.true
  • neq — diferente: ?status=neq.deleted
  • gt, gte, lt, lte — comparações: ?age=gt.18
  • like, ilike — pattern matching: ?name=ilike.*silva*
  • is — NULL/TRUE: ?deleted_at=is.null
  • in — lista: ?status=in.(active,pending)

INSERT

curl
curl -X POST https://api.superdb.com.br/tasks \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Prefer: return=representation" \
  -d '{"title": "Nova tarefa", "done": false}'

UPDATE

curl
curl -X PATCH "https://api.superdb.com.br/tasks?id=eq.UUID" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"done": true}'

DELETE

curl
curl -X DELETE "https://api.superdb.com.br/tasks?id=eq.UUID" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

Joins automáticos

PostgREST detecta foreign keys e permite joins por sintaxe tabela(colunas):

curl
curl "https://api.superdb.com.br/tasks?select=*,user:users(name,email)" \
  -H "Authorization: Bearer $DATA_PLANE_TOKEN"

Preview no Studio

Toda chamada REST roda como SQL embaixo. Você pode espiar no Studio:

superdb.com.br/dash/meuapp/api-logs
v0.4.2
GET /tasks?done=eq.false ×
1-- traduzido pelo PostgREST
2select * from proj_meuapp.tasks
3 where done = false
4 and user_id = auth.uid();

Limites

  • Máximo de 1.000 registros por request por padrão
  • Use limit e offset pra paginar
  • Use Range header pra range requests

Requisições por segundo

O limite de requisições por segundo é por projeto, não por endereço de IP — um app renderizado no servidor faz todas as chamadas de um endereço só, e contar por IP transformaria o limite do seu app inteiro no limite de um usuário. O número de cada plano está na página de preços.

Ao ultrapassar, a API responde 429 com o cabeçalho Retry-After em segundos, e um corpo que diz qual plano e qual limite foram aplicados:

HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-SuperDB-Plano: pro
X-SuperDB-Limite-Rps: 500

{
  "error": "rate_limited",
  "message": "Limite de 500 requisições por segundo do plano pro atingido.",
  "hint": "Aguarde um segundo e tente de novo, ou mude de plano em https://superdb.com.br/precos"
}
💡

O SDK já trata isso. O @superdb/client lê o Retry-After e reenvia sozinho quando a espera é curta. Espera longa ele devolve para você decidir — é assim que ele distingue uma rajada momentânea de um bloqueio deliberado, como o de tentativas de login.

💡

Dica: Para queries tipadas, use o SDK @superdb/client (com os nomes do supabase-js) com createClient(url, anonKey, { project: 'meuapp' }) — o .from() monta as URLs PostgREST e manda o Accept-Profile automaticamente, sem header manual.

Essa página ajudou?