API REST
Visão geral
Seção intitulada “Visão geral”O Rebase gera automaticamente uma API completa a partir das definições das suas coleções:
- API REST — Endpoints CRUD para cada coleção em
/api/data/:slug - Especificação OpenAPI — Especificação legível por máquina em
/api/docs - Swagger UI — Explorador interativo de API em
/api/swagger(apenas em modo de desenvolvimento)
Nenhum código é necessário — defina suas coleções e a API surge automaticamente.
Endpoints REST
Seção intitulada “Endpoints REST”Para cada coleção, os seguintes endpoints são gerados. Todas as outras rotas montadas pelo backend — auth, storage, admin, meta — estão no índice de endpoints.
| Método | Caminho | Descrição |
|---|---|---|
GET |
/api/data/:slug |
Listar entidades |
POST |
/api/data/:slug/query |
A mesma listagem, com seus parâmetros de consulta em um corpo JSON — para uma consulta longa demais para uma URL. Veja Consultas longas demais para uma URL |
GET |
/api/data/:slug/count |
Contar entidades |
GET |
/api/data/:slug/aggregate |
count(), sum(), avg(), min(), max(), opcionalmente agrupados. Aceita os mesmos filtros que o endpoint de listagem, e o RLS se aplica às linhas que estão sendo agregadas — veja Consultas |
GET |
/api/data/:slug/:id |
Obter uma única entidade |
POST |
/api/data/:slug |
Criar um registro |
PATCH |
/api/data/:slug/:id |
Atualizar um registro (parcial — apenas as propriedades enviadas são gravadas) |
DELETE |
/api/data/:slug/:id |
Excluir um registro |
POST |
/api/data/:slug/bulk |
Criar várias entidades em uma única transação |
PATCH |
/api/data/:slug/bulk |
Atualizar várias entidades em uma única transação |
POST |
/api/data/:slug/bulk/delete |
Excluir várias entidades em uma única transação |
POST |
/api/data/_batch |
Gravar entre coleções em uma única transação |
Rotas de Subcoleções
Seção intitulada “Rotas de Subcoleções”Relações aninhadas são acessíveis por meio de caminhos de URL:
GET /api/data/authors/42/posts → list author's postsPOST /api/data/authors/42/posts/query → the same listing, parameters in the body (0.24+)POST /api/data/authors/42/posts/count → the same count, parameters in the body (0.24+)GET /api/data/authors/42/posts/7 → get a specific post by authorPOST /api/data/authors/42/posts → create a post for authorPATCH /api/data/authors/42/posts/7 → update the postDELETE /api/data/authors/42/posts/7 → delete the postMecânica de Roteamento e Análise de Segmentos
Seção intitulada “Mecânica de Roteamento e Análise de Segmentos”Para lidar com profundidades arbitrárias de subcoleções aninhadas, o Rebase roteia requisições recebidas usando a regex de parâmetro :rest{.+} do Hono. O mecanismo interno de análise de segmentos analisa os caminhos contando segmentos separados por barra:
- Contagem ímpar de segmentos (ex.:
authors/42/posts-> 3 segmentos) representa uma requisição de listagem de coleção. - Contagem par de segmentos (ex.:
authors/42/posts/7-> 4 segmentos) representa uma operação em um ID de entidade específico. O último segmento é extraído como oentityIdde destino.
O mecanismo filtra namespaces reservados do sistema (ex.: history) da análise de segmentos de caminho para evitar colisões com endpoints integrados.
Autenticação
Seção intitulada “Autenticação”Todos os endpoints de dados exigem autenticação por padrão. Inclua um token Bearer no cabeçalho Authorization:
curl -H "Authorization: Bearer <access-token>" \ https://api.example.com/api/data/productsPara chamadas de servidor para servidor, use a chave de serviço:
curl -H "Authorization: Bearer <service-key>" \ https://api.example.com/api/data/productsA filtragem de uma lista — operadores ?field=op.value, grupos or/and, o dialeto JSON where e como se combinam — tem uma página própria.
Ordenação
Seção intitulada “Ordenação”Use orderBy com o formato field:direction:
# Sort by price descendingGET /api/data/products?orderBy=price:desc
# Sort by name ascending (default)GET /api/data/products?orderBy=name:ascUma direção ausente é asc. Uma direção que não seja nem asc nem desc, ou um
campo que a coleção não possui, resulta em um 400 — e não em um 200 com as linhas na
ordem que o banco de dados bem entender, o que seria indistinguível de uma ordenação que funcionou.
Múltiplas chaves
Seção intitulada “Múltiplas chaves”A forma abreviada suporta uma chave. Para mais chaves, passe um array JSON — a segunda chave serve de desempate entre linhas consideradas iguais pela primeira:
# By category, and newest first within each categoryGET /api/data/products?orderBy=[{"field":"category"},{"field":"createdAt","direction":"desc"}]Ambas as grafias funcionam em todas as rotas que listam linhas, incluindo as aninhadas
(/api/data/authors/:id/posts). Toda ordenação termina no id da linha em ordem decrescente,
quer você tenha solicitado ou não: é isso que torna a ordenação total, e paginar sobre
uma ordem que não é total repete e pula linhas.
Um parâmetro ?orderBy= repetido não é uma ordenação de múltiplas chaves — o último
prevalece, assim como ocorre com qualquer outro parâmetro de consulta. Use o array.
Onde os NULLs são ordenados
Seção intitulada “Onde os NULLs são ordenados”Por padrão, os NULLs são ordenados por último em ordem ascendente e primeiro em ordem descendente, que é a própria convenção do Postgres. Um terceiro segmento separado por dois-pontos define o contrário:
# Newest first, with the undated rows at the end rather than the topGET /api/data/posts?orderBy=publishedAt:desc:lastA forma de array JSON aceita uma chave "nulls" para a mesma finalidade:
GET /api/data/posts?orderBy=[{"field":"publishedAt","direction":"desc","nulls":"last"}]Qualquer valor diferente de first ou last resulta em um 400, e não em uma ordem
silenciosamente diferente. O cursor abaixo respeita o que a ordenação declarou, de modo
que a paginação sobre uma chave anulável permanece correta em qualquer posicionamento.
Paginação
Seção intitulada “Paginação”Use limit e offset, ou page — um ou outro:
# Limit and offsetGET /api/data/products?limit=20&offset=40
# Page-based (uses the default limit of 50)GET /api/data/products?page=3O limite padrão é 50, e o máximo é 1000. Ambos provêm de DEFAULT_LIST_LIMIT / MAX_LIST_LIMIT, que a especificação OpenAPI gerada também relata — um limit acima do máximo é rejeitado em vez de ser ajustado/limitado.
Todos os três parâmetros de janela são recusados em vez de reparados, e cada um
identifica a si próprio: INVALID_LIMIT, INVALID_OFFSET (um número inteiro maior ou igual a 0) e
INVALID_PAGE (um número inteiro maior ou igual a 1). Uma janela silenciosamente
diferente da solicitada não pode ser diferenciada de ter chegado ao final da coleção,
razão pela qual nenhum deles é ajustado ou ignorado.
Pelo mesmo motivo, page e offset juntos resultam em um 400
PAGE_WITH_OFFSET, em vez de um deles prevalecer silenciosamente.
Ignorando a contagem
Seção intitulada “Ignorando a contagem”meta.total é um COUNT(*) sobre todas as linhas correspondentes — numa tabela
grande, a maior parte do custo de uma requisição de listagem. ?count=none
(GET /api/data/orders?count=none) a dispensa: sem meta.total, enquanto
meta.hasMore continua exato (a leitura pede uma linha além da página).
?count=exact é o padrão; qualquer outro valor resulta em um 400
INVALID_COUNT. Uma leitura com ?distinct=true nunca é contada, nem uma busca vetorial com vector_threshold (veja Busca Vetorial).
Paginação por cursor
Seção intitulada “Paginação por cursor”O offset reconta as linhas a cada requisição, portanto, uma linha inserida ou excluída
entre duas páginas desloca a janela e a navegação pula ou repete linhas silenciosamente.
Em vez disso, ?after= faz uma busca direta (seek): a próxima página começa estritamente
após a última linha entregue.
Toda resposta de listagem traz meta.nextCursor enquanto houver outra página. Envie-o
de volta inalterado:
GET /api/data/orders?orderBy=createdAt:desc&limit=100# → meta.nextCursor = "eyJrIjpbWyJjcmVhdGVkX2F0Iiw…"
GET /api/data/orders?orderBy=createdAt:desc&limit=100&after=eyJrIjpbWyJjcmVhdGVkX2F0Iiw…O cursor é opaco — ele codifica as chaves de ordenação e os valores da última linha para elas —, portanto três regras se aplicam, cada uma resultando em um 400 em vez de uma página incorreta:
| Situação | Código |
|---|---|
after com offset ou page |
CURSOR_WITH_OFFSET — ambos definem onde a página começa |
after com um orderBy diferente daquele sob o qual foi emitido |
CURSOR_ORDER_MISMATCH |
| Um cursor que esta API não emitiu | INVALID_CURSOR |
Uma requisição que não especifica nenhum orderBy adota o do cursor, portanto passar
meta.nextCursor de volta sem reafirmar a ordenação funciona.
Ordenações por múltiplas chaves e chaves anuláveis paginam corretamente: a comparação é
construída sobre cada chave em ordem, com o posicionamento de NULL que a ordenação declarou. A
única ordenação que nenhum cursor pode descrever é a relevância (_score) — calculada por consulta
e não armazenada em lugar nenhum —, e essa listagem simplesmente não traz nenhum nextCursor.
Selecionando colunas
Seção intitulada “Selecionando colunas”?fields= restringe a leitura às colunas nomeadas. É uma projeção aplicada diretamente
à consulta, e não um corte na resposta:
GET /api/data/posts?fields=id,title&limit=50A chave primária sempre é retornada (uma linha que não pode ser endereçada não pode ser
atualizada, excluída ou paginada adiante — e o cursor é derivado dela), e as colunas com
excludeFromApi permanecem ocultas, quer sejam nomeadas ou não. Uma coluna desconhecida
resulta em um 400 UNKNOWN_FIELD, em vez de uma linha silenciosamente sem um campo.
Uma relação nomeada em ?include= volta independentemente de ?fields= a nomear:
?include=author&fields=title retorna title, a chave e author. Nomear a
relação também em ?fields= é permitido e não muda nada.
?distinct=true agrupa linhas idênticas com base nessas colunas:
# The statuses actually in useGET /api/data/posts?fields=status&distinct=trueEle é recusado (400) quando utilizado junto a um searchString com classificação ou a uma busca
vetorial, que anexam uma pontuação por linha que torna cada linha distinta por definição, e quando
orderBy especifica uma coluna que fields não retorna (DISTINCT_ORDER_BY_NOT_SELECTED) — o
Postgres não pode ordenar uma leitura DISTINCT por uma expressão fora da sua lista de seleção.
?fields= e ?distinct= também funcionam na rota de busca por ID e nas rotas de
subcoleções aninhadas.
Formato da Resposta
Seção intitulada “Formato da Resposta”As respostas de listagem incluem metadados de paginação:
{ "data": [ { "id": 1, "name": "Widget", "price": 29.99 }, { "id": 2, "name": "Gadget", "price": 49.99 } ], "meta": { "total": 150, "limit": 20, "offset": 0, "hasMore": true, "nextCursor": "eyJrIjpbWyJpZCIsImRlc2MiXV0sInYiOnsiaWQiOjJ9LCJpIjoyfQ" }}nextCursor está presente enquanto hasMore for true e a página tiver retornado pelo
menos uma linha; ele fica ausente na última página e em ordenações que nenhum cursor
pode descrever.
As respostas de entidade única retornam um objeto simples:
{ "id": 1, "name": "Widget", "price": 29.99, "createdAt": "2026-01-15T10:30:00Z"}Toda falha, de qualquer rota, retorna em um único envelope:
{ "error": { "message": "Unknown filter operator 'contains' on field 'title'.", "code": "UNKNOWN_FILTER_OPERATOR", "details": { "field": "title", "operator": "contains" }, "requestId": "9f1c0b8e-4d2a-4e1b-9d0f-2c7a5b3e6a11" }}message e code estão sempre presentes. details aparece quando a recusa é
sobre algo — o campo incorreto, os caminhos que falharam. requestId aparece
quando a requisição continha um cabeçalho X-Request-ID ou quando um lhe foi atribuído;
ele também é refletido no cabeçalho da resposta e é a referência a ser citada em um relatório de bug.
Faça o tratamento condicional com base no code, nunca na message ou apenas no status.
Os códigos estão em SCREAMING_SNAKE_CASE e são estáveis; as mensagens são escritas para uma
pessoa lendo um console e estão sujeitas a alterações. O status HTTP fica na resposta, não no corpo.
| Status | Código comum | Significado |
|---|---|---|
| 400 | BAD_REQUEST, INVALID_INPUT, INVALID_LIMIT, INVALID_OFFSET, INVALID_PAGE |
A requisição está malformada ou solicita algo impossível |
| 401 | UNAUTHORIZED |
Nenhuma credencial informada, ou uma credencial que não identifica ninguém |
| 403 | FORBIDDEN, SCOPE_MISSING, WRITE_DENIED |
Uma credencial que identifica alguém sem a devida permissão |
| 404 | NOT_FOUND |
O recurso solicitado não existe |
| 409 | CONFLICT |
Conflito de estado — chave duplicada, árvore desatualizada (dirty tree) |
| 501 | varia | O recurso existe, mas não está configurado nesta implantação |
| 503 | SERVICE_UNAVAILABLE |
Uma dependência está fora do ar; a requisição nunca chegou até ela |
Uma funcionalidade ausente porque esta implantação não a habilitou responde com 501 com um código e um motivo, e não 404 — um 404 não explicado em uma rota recém-chamada pela interface pareceria uma falha no deploy.
As rotas adicionam seus próprios códigos mais específicos sobre estes (EMAIL_EXISTS,
TOKEN_EXPIRED, UNKNOWN_FILTER_OPERATOR, …), portanto considere a lista de códigos
como aberta. O SDK tipado converte todos eles em um único RebaseApiError contendo
status, code e details — veja
Tratamento de erros.
Busca Textual
Seção intitulada “Busca Textual”Use searchString para busca de texto completo (full-text search) em campos de texto:
GET /api/data/products?searchString=wireless%20keyboardBusca Vetorial
Seção intitulada “Busca Vetorial”Uma coleção com uma propriedade vector aceita uma busca por vizinhos mais próximos na
listagem: ?vector_search=embedding&vector=[0.15,0.22,-0.05], com vector_distance e
vector_threshold opcionais. Os parâmetros, os limites e o comportamento dos filtros sobre
o índice estão em Busca Vetorial.
Consultas longas demais para uma URL
Seção intitulada “Consultas longas demais para uma URL”POST /api/data/:slug/query (esta listagem) e POST /api/data/:slug/count
recebem os parâmetros de consulta em um corpo JSON, para uma consulta cuja URL não caberia — um embedding, uma lista in longa.
Veja Consultas longas demais para uma URL.
Inclusão de Relações
Seção intitulada “Inclusão de Relações”Use o parâmetro include para incorporar entidades relacionadas:
# Include specific relationsGET /api/data/articles?include=author,categories
# Include all relations, one hop deepGET /api/data/articles?include=*
# A relation of a relation — up to three hopsGET /api/data/articles?include=comments.authorUm nome que não seja uma relação da coleção resulta em um 400
UNKNOWN_RELATION, em qualquer nível. Anteriormente isso era ignorado, o que
respondia com 200 trazendo o campo simplesmente ausente — indistinguível de uma linha
que genuinamente não tem nenhuma linha relacionada, de modo que um erro de digitação parecia
exatamente dados vazios. Um caminho com profundidade superior a três saltos resulta em INCLUDE_TOO_DEEP.
Restringindo uma relação
Seção intitulada “Restringindo uma relação”A forma separada por vírgulas não tem onde especificar um limit por relação,
portanto include também aceita JSON — diferenciado por uma chave de abertura no início:
GET /api/data/posts?include={"comments":{"limit":5,"where":{"published":["==",true]},"orderBy":"createdAt:desc","fields":["id","body"],"include":{"author":true}}}| Chave | Significado |
|---|---|
limit |
Linhas por linha pai, não em toda a página. Aplicado pela própria consulta, portanto as linhas além dele nunca são lidas do banco de dados |
where |
O mesmo dialeto de filtro que o where de nível superior usa |
logical |
Um grupo or/and/not sobre as linhas relacionadas |
orderBy |
A mesma grafia de ordenação, incluindo o posicionamento de NULL. Os empates, e um limit sem orderBy, seguem a chave da linha relacionada, em ordem ascendente |
fields |
Colunas da linha relacionada; sua chave primária sempre permanece |
include |
Relações da linha relacionada, por sua vez |
count |
true também devolve o total de cada pai em _count ("_count":{"comments":37}), contado pela mesma instrução. Apenas relações para-muitos. Veja Contando o que um limit deixa de fora |
true significa “carregar por completo”, portanto {"author":true} e author são a
mesma requisição. Ambas as grafias funcionam na rota de listagem, na rota de busca por ID
e nas rotas de subcoleções aninhadas.
Cada salto é uma consulta em lote para toda a página, nunca uma por linha.
As relações incluídas são incorporadas diretamente na resposta:
{ "id": 1, "title": "Getting Started", "authorId": 42, "author": { "id": 42, "name": "Jane Doe", "email": "jane@example.com", "companyId": 7 }}Uma linha incluída é o que a rota do próprio alvo (GET /api/data/authors/42)
serve: as mesmas colunas e codificações, chaves estrangeiras como colunas, sem objetos de referência.
Escrita
Seção intitulada “Escrita”Chaves de idempotência, escritas condicionais (ETag / If-Match), operações de campo
($inc, $push, $pull, $merge), upsert em chave natural,
Prefer: return=minimal e o endpoint entre coleções POST /api/data/_batch
estão todos em sua própria página: Escrita via REST.
Pipeline de Hooks do Ciclo de Vida
Seção intitulada “Pipeline de Hooks do Ciclo de Vida”No Postgres, toda mutação REST (POST, PATCH, DELETE) roda os callbacks da sua coleção em ordem, dentro da única transação que carrega a escrita:
Request ──► BEGIN ──► beforeSave/beforeDelete ──► DB operation ──► afterSave/afterDelete ──► COMMIT ──► ResponseTodo hook é aguardado, e um erro lançado por qualquer um deles desfaz a escrita inteira:
beforeSave,beforeDeleterodam antes da escrita. Eles podem alterar os valores de entrada, validar ou recusar a requisição lançando um erro — quem chamou recebe 400CALLBACK_REJECTEDe nada é escrito.afterSave,afterDeleterodam depois da escrita, mas antes do commit. Um erro reverte a linha e responde o mesmo 400, comdetails.stageindicando o hook. Eles mantêm a transação aberta enquanto rodam, por isso são o lugar errado para um webhook ou qualquer outra chamada de rede — Hooks explica para onde vai esse trabalho.
Endpoints do sistema
Seção intitulada “Endpoints do sistema”| Método | Caminho | Autenticação | Descrição |
|---|---|---|---|
GET |
/health e /api/health |
nenhuma | Verificação de vivacidade/prontidão (liveness/readiness check) |
GET |
/api/docs |
nenhuma; schema:read quando enableSwagger é false (o padrão do runtime em produção) |
A especificação OpenAPI 3.0 em JSON |
GET |
/api/swagger |
nenhuma | Swagger UI. Servido apenas fora de produção; REBASE_ENABLE_SWAGGER não consegue ativá-lo em produção. Não é servido quando enableSwagger é false, nem em desenvolvimento |
GET |
/api/meta/schema-version |
nenhuma | O hash do schema a partir do qual este backend foi construído, e a versão do runtime — deliberadamente não autenticado, e nada que descreva o schema |
GET |
/api/meta/contract |
schema:read: um admin, a chave de serviço ou uma chave de API que o tenha |
O contrato completo da coleção, para rebase generate-sdk --from. Comportamento fail-closed: 404 quando nenhuma autenticação está configurada |
GET |
/metrics |
REBASE_METRICS_TOKEN quando definido |
Métricas do Prometheus, quando REBASE_METRICS=true |
OpenAPI / Swagger
Seção intitulada “OpenAPI / Swagger”A especificação OpenAPI é gerada automaticamente a partir das definições das suas coleções: ela descreve os endpoints de listagem, consulta, contagem, agregação, leitura, criação, atualização, exclusão e operações em lote de cada coleção atendida pelo backend, suas rotas aninhadas e _batch, com seus parâmetros de consulta e esquemas de resposta. Não se trata de um mapa completo de toda a superfície HTTP — as rotas de auth, storage, functions e cron estão documentadas apenas neste site — e colunas marcadas como excludeFromApi são omitidas dela.
Para uma coleção com softDelete, a especificação diz
que uma exclusão, individual ou em lote, move a linha para a lixeira, e documenta
?hard=true nas duas exclusões e ?deleted=include|only na listagem, na leitura
individual, na contagem e na agregação. O explorador de API do Studio envia apenas os
parâmetros que a especificação nomeia, então é isso que permite a ele expurgar uma linha ou listar a lixeira.
Chamadores automatizados (machine callers) se autenticam com uma chave com escopo específico em vez de uma sessão: Chaves de API.
Metadados do Schema
Seção intitulada “Metadados do Schema”O schema completo das coleções do projeto — cada coleção, propriedade e relação — é servido a um administrador autenticado:
GET /api/meta/contractEle requer o escopo schema:read, que um admin tem, e em uma implantação sem autenticação configurada ele
não é disponibilizado de forma alguma (404 CONTRACT_UNAVAILABLE), evitando
expor o schema publicamente. Seu endpoint irmão retorna uma string de versão que
representa o schema sem descrevê-lo, sendo deliberadamente acessível sem credenciais
— que é o que um job de CI consulta periodicamente:
GET /api/meta/schema-versionPara o formato dos endpoints em vez do schema por trás deles, o documento OpenAPI
fica em GET /api/docs, com o Swagger UI em /api/swagger fora de
produção.
Próximos passos
Seção intitulada “Próximos passos”- SDK tipado — Cliente com tipagem estática (type-safe) para a API REST
- Coleções — Defina o schema dos seus dados
- Regras de Segurança (RLS) — Controle o acesso por linha