Pular para o conteúdo
🧭 CONCEITOS

PostgREST — o tradutor de SQL pra REST.

PostgREST é um servidor open-source que expõe seu Postgres como API REST. Cada tabela vira um endpoint, cada coluna vira filtro, FKs viram joins automáticos. Sem escrever endpoint. Mas tem limites — saiba quando usar SQL direto.

O que é?

PostgREST é um servidor open-source escrito em Haskell que lê o schema do seu Postgres e gera uma API REST automaticamente. Você não escreve nenhuma linha de controller ou handler.

O mapeamento é direto:

PostgresHTTP
Tabela tasksEndpoint /tasks
Coluna titleFiltro ?title=eq.foo
FK tasks.author_id → usersEmbed ?select=*,author:users(*)
ViewEndpoint read-only (ou writable com triggers)
FunctionEndpoint /rpc/<function>

Você cria a tabela. PostgREST detecta. API existe.

Por que importa?

  • Zero código de CRUD. Quanto tempo você passou escrevendo controllers list/get/create/update/delete? PostgREST elimina isso.
  • RLS aplicada automaticamente. Cada request herda o JWT — não tem onde "esquecer" autorização.
  • Performance comparável a query direta. PostgREST gera SQL otimizado e usa connection pooling — overhead da camada REST é minúsculo.
  • Schema é a fonte da verdade. Mudou a tabela? API muda junto. Sem migrar 2 lugares.
  • Auto-discovery. Cliente pode ver o OpenAPI completo em GET / — útil pra clients gerados automaticamente.

Como funciona no SuperDB

O PostgREST roda como serviço separado no SuperDB, conectado ao seu Postgres. Ele:

  1. Recebe HTTP request com JWT no header Authorization
  2. Valida assinatura do JWT contra o secret do projeto
  3. Inicia transação Postgres com SET ROLE = role do JWT (authenticated ou anon)
  4. Seta SET request.jwt.claims pra RLS ler
  5. Traduz a URL pra SQL e executa
  6. Retorna JSON
  7. Commit / rollback

O db.from(...) do SDK é só uma camada que monta a URL:

SDK → URL → SQL
// SDK
db.from('tasks').select('id, title').eq('done', false).limit(10)

// URL gerada
GET /tasks?select=id,title&done=eq.false&limit=10

// SQL executado
SELECT id, title FROM tasks WHERE done = false LIMIT 10
-- + filtros RLS adicionados pelo Postgres

Quando usar (e quando não)

OperaçãoPostgREST?Alternativa
CRUD simplesSim — é pra isso
Filtros, ordenação, paginaçãoSim, rico
Join via FKEmbed automático
Upsert?on_conflict=...
Full-text searchSim, via ?col=fts.termoView com tsvector
Agregação (sum, avg)Não diretoSQL function (/rpc/)
Transação multi-tabela complexaNão cabe na URLSQL function (/rpc/)
Lógica condicional / loopREST não é Turing-completeSQL function ou app backend
💡

Regra de bolso: se cabe em uma SELECT simples ou um INSERT/UPDATE direto, usa PostgREST. Se precisa de BEGIN/COMMIT ou múltiplas queries coordenadas, vira SQL function e chama via db.rpc().

Exemplos práticos

1. SELECT com filtro

HTTP
GET /tasks?select=id,title,due_at&done=eq.false&due_at=lte.2026-12-31&order=due_at.asc&limit=20

Authorization: Bearer eyJhbGciOi...
SDK equivalente
const { data } = await db
  .from('tasks')
  .select('id, title, due_at')
  .eq('done', false)
  .lte('due_at', '2026-12-31')
  .order('due_at', { ascending: true })
  .limit(20)

2. INSERT

HTTP
POST /tasks
Content-Type: application/json
Prefer: return=representation

{ "title": "Comprar café", "due_at": "2026-05-20" }

3. Join automático por FK

Se você tem posts.author_id → users.id como FK:

HTTP — embed
GET /posts?select=id,title,author:users(name,avatar_url)

[
  {
    "id": "p1",
    "title": "Lançamento",
    "author": { "name": "Felipe", "avatar_url": "..." }
  }
]

Armadilhas comuns

  • Limit padrão é 1000. Pediu sem limit? Recebe no máximo 1000 rows. Sempre pagine ou seja explícito.
  • count=exact em tabela grande é lento. PostgREST roda um SELECT count(*) que escaneia tudo. Pra tabela com milhões de rows, use count=estimated.
  • Sem FK, sem embed. Join automático depende de FK definida. posts.author_id sem REFERENCES users(id) não permite ?select=*,author:users(*).
  • Order em coluna não indexada é lento. ?order=created_at.desc em tabela com 1M rows sem índice em created_at = full scan.
  • OR não é trivial. ?or=(status.eq.open,priority.eq.high) — sintaxe específica, não é o que você pensa.
  • Modificar schema invalida cache. PostgREST faz cache do schema na inicialização. Adicionou tabela? Mande NOTIFY pgrst, 'reload schema'; ou aguarde (SuperDB faz isso automaticamente).
  • Coluna gerada (GENERATED ALWAYS) bloqueia INSERT. Tente passar valor pra ela = erro 400. Use ?select=col1,col2 no INSERT pra ignorar.
  • Não tem WebSocket. PostgREST é REST puro. Pra updates ao vivo, use o Realtime (que escuta WAL e empurra pro client).
⚠️

Não exponha tudo. PostgREST expõe todas as tabelas do schema por padrão. Se você tem internal_audit_log que não quer público, ou cria em outro schema (não exposto), ou REVOKE select da role anon / authenticated.

Termos relacionados

Essa página ajudou?