Salta ai contenuti

API REST

Rebase genera automaticamente un’API completa dalle definizioni delle tue collezioni:

  • API REST — Endpoint CRUD per ogni collezione su /api/data/:slug
  • Specifica OpenAPI — Specifica leggibile dalla macchina su /api/docs
  • Swagger UI — Esploratore API interattivo su /api/swagger (solo in modalità sviluppo)

Non è richiesto alcun codice — definisci le tue collezioni e l’API appare automaticamente.

Per ogni collezione vengono generati i seguenti endpoint:

Metodo Percorso Descrizione
GET /api/data/:slug Elencare le entità
GET /api/data/:slug/count Contare le entità
GET /api/data/:slug/:id Ottenere una singola entità
POST /api/data/:slug Creare un’entità
PUT /api/data/:slug/:id Aggiornare un’entità
DELETE /api/data/:slug/:id Eliminare un’entità

Le relazioni annidate sono accessibili tramite percorsi URL:

GET /api/data/authors/42/posts → list author's posts
GET /api/data/authors/42/posts/7 → get a specific post by author
POST /api/data/authors/42/posts → create a post for author
PUT /api/data/authors/42/posts/7 → update the post
DELETE /api/data/authors/42/posts/7 → delete the post

Per gestire profondità arbitrarie di sottocollezioni annidate, Rebase instrada le richieste in arrivo usando la regex del parametro :rest{.+} di Hono. Il motore interno di analisi dei segmenti analizza i percorsi contando i segmenti separati da slash:

  • Numero dispari di segmenti (ad es. authors/42/posts -> 3 segmenti) rappresenta una richiesta di elenco di collezione.
  • Numero pari di segmenti (ad es. authors/42/posts/7 -> 4 segmenti) rappresenta un’operazione su un ID di entità specifico. L’ultimo segmento viene estratto come entityId target.

Il motore filtra i namespace di sistema riservati (ad es. history) dall’analisi dei segmenti del percorso per prevenire collisioni con gli endpoint integrati.

Tutti gli endpoint dei dati richiedono l’autenticazione per impostazione predefinita. Includi un token Bearer nell’header Authorization:

curl -H "Authorization: Bearer <access-token>" \
https://api.example.com/api/data/products

Per le chiamate server-to-server, usa la chiave di servizio:

curl -H "Authorization: Bearer <service-key>" \
https://api.example.com/api/data/products

Usa i parametri di query in stile PostgREST per filtrare i risultati. Il formato è ?field=operator.value:

# Exact match
GET /api/data/products?active=eq.true
# Comparison operators
GET /api/data/products?price=gt.100
GET /api/data/products?price=lte.50
# Multiple filters (AND)
GET /api/data/products?active=eq.true&price=gt.10
# IN operator — match any value in a set
GET /api/data/products?status=in.(draft,published)
# NOT IN
GET /api/data/products?status=nin.(archived,deleted)
# Array contains
GET /api/data/products?tags=cs.electronics
# Array contains any
GET /api/data/products?tags=csa.(electronics,books)
Operatore Significato Esempio
eq Uguale (==) ?active=eq.true
neq Diverso (!=) ?status=neq.draft
gt Maggiore di (>) ?price=gt.100
gte Maggiore o uguale (>=) ?price=gte.100
lt Minore di (<) ?price=lt.50
lte Minore o uguale (<=) ?price=lte.50
in In array ?status=in.(a,b,c)
nin Non in array ?status=nin.(a,b)
cs L’array contiene ?tags=cs.value
csa L’array contiene uno ?tags=csa.(a,b)

Usa or e and per condizioni complesse:

# OR: match products that are either cheap or on sale
GET /api/data/products?or=(price.lt.10,on_sale.eq.true)
# AND: explicit conjunction
GET /api/data/products?and=(active.eq.true,price.gt.0)

Usa orderBy con il formato field:direction:

# Sort by price descending
GET /api/data/products?orderBy=price:desc
# Sort by name ascending (default)
GET /api/data/products?orderBy=name:asc

Usa limit e offset, oppure page:

# Limit and offset
GET /api/data/products?limit=20&offset=40
# Page-based (uses default limit of 20)
GET /api/data/products?page=3

Il limite predefinito è 20, il massimo è 100.

Le risposte di elenco includono metadati di paginazione:

{
"data": [
{ "id": 1, "name": "Widget", "price": 29.99 },
{ "id": 2, "name": "Gadget", "price": 49.99 }
],
"meta": {
"total": 150,
"limit": 20,
"offset": 0,
"hasMore": true
}
}

Le risposte per una singola entità restituiscono un oggetto piatto:

{
"id": 1,
"name": "Widget",
"price": 29.99,
"created_at": "2026-01-15T10:30:00Z"
}

Usa searchString per la ricerca full-text sui campi di tipo stringa:

GET /api/data/products?searchString=wireless%20keyboard

Se una collezione definisce una proprietà di tipo vector, puoi eseguire ricerche di similarità ad alta velocità usando operazioni di distanza pgvector compilate direttamente nella query del database.

GET /api/data/products?vector_search=embedding&vector=[0.15,0.22,-0.05]&vector_distance=cosine&vector_threshold=0.8
Parametro Tipo Descrizione
vector_search string Il nome della proprietà vettoriale su cui eseguire la query.
vector string Un array di float serializzato in JSON che rappresenta il vettore di query.
vector_distance string La metrica di distanza da valutare. Valori supportati: cosine (predefinito, <=>), l2 (<->), inner_product (<#>).
vector_threshold number Soglia massima di distanza. Vengono restituiti solo i record con una distanza inferiore a questa soglia.

Usa il parametro include per incorporare le entità correlate:

# Include specific relations
GET /api/data/articles?include=author,categories
# Include all relations
GET /api/data/articles?include=*

Le relazioni incluse vengono incorporate direttamente nella risposta:

{
"id": 1,
"title": "Getting Started",
"author_id": 42,
"author": {
"id": 42,
"name": "Jane Doe",
"email": "jane@example.com"
}
}

Usa fields per selezionare colonne specifiche:

GET /api/data/products?fields=id,name,price

Ogni operazione di mutazione REST (POST, PUT, DELETE) attraversa una pipeline di esecuzione degli hook rigorosa e sequenziale:

Request ──► beforeSave/beforeDelete (blocking) ──► DB Operation ──► afterSave/afterDelete (deferred) ──► Response
  1. Hook bloccanti (beforeSave, beforeDelete) Questi hook vengono eseguiti in modo sincrono nel ciclo principale della richiesta prima di confermare la transazione del database. Possono modificare i payload in arrivo, eseguire validazioni personalizzate o interrompere completamente la richiesta generando un errore.

  2. Hook differiti (afterSave, afterDelete) Questi hook vengono eseguiti in modo asincrono dopo che la transazione del database è stata confermata con successo. Usano promise differite (fire-and-forget), il che significa che vengono eseguiti in background e non bloccano la risposta HTTP del client. Ideali per inviare webhook, attivare notifiche push o accodare attività esterne.

  • Specifica OpenAPI: GET /api/docs — Restituisce la specifica JSON OpenAPI 3.0 completa
  • Swagger UI: GET /api/swagger — Esploratore API interattivo (solo in modalità sviluppo)

La specifica OpenAPI viene generata automaticamente dalle definizioni delle tue collezioni e include tutti gli endpoint, i parametri di query e gli schemi di risposta.

Le chiavi API forniscono l’autenticazione machine-to-machine per agenti, server MCP, pipeline CI e integrazioni esterne. Supportano l’ambito dei permessi per collezione e l’accesso amministratore completo opzionale.

# Via CLI
rebase api-keys create --name "My Integration" \
--permissions '[{"collection":"orders","operations":["read","write"]}]'
# Via REST (requires admin auth)
curl -X POST http://localhost:3000/api/admin/api-keys \
-H "Authorization: Bearer <service-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "My Integration",
"permissions": [{ "collection": "orders", "operations": ["read", "write"] }]
}'

La risposta include la chiave completa in testo semplice (rk_live_...) esattamente una volta — memorizzala immediatamente.

curl http://localhost:3000/api/data/orders \
-H "Authorization: Bearer rk_live_abc123..."

La richiesta di una chiave API attraversa due controlli di autorizzazione, ed entrambi devono consentirla:

  1. L’elenco dei permessi della chiave — collezione × operazione, controllato a livello di route.
  2. Sicurezza a livello di riga — le chiavi API non bypassano la RLS. Una chiave viene eseguita come uid: "api-key:<id>" con il ruolo service (più admin quando admin: true). Le chiavi admin passano tramite le politiche admin integrate; una chiave non-admin vede solo le righe che una regola di sicurezza concede esplicitamente al ruolo service o al pubblico. Le regole in stile proprietario (owner_id = auth.uid()) non corrispondono mai a una chiave API.

Quindi una chiave non-admin con permessi "*" può comunque ottenere risultati vuoti — è la RLS che funziona, non un bug. Concedi il ruolo service nelle regole di sicurezza delle collezioni pertinenti, oppure usa una chiave admin.

Le invocazioni di funzioni hanno un ambito come le collezioni, sotto il namespace functions: {"collection": "functions", "operations": ["write"]} concede ogni funzione, "functions/<name>" ne concede una, e il carattere jolly globale "*" le concede tutte. Una chiave senza tale voce non può invocare funzioni affatto.

L’archiviazione funziona allo stesso modo, sotto il namespace storage: {"collection": "storage", "operations": ["read", "write"]} consente alla chiave di scaricare/elencare (read), caricare e creare cartelle (write), ed eliminare file (delete). Il carattere jolly globale "*" concede anche l’archiviazione. Una chiave senza tale voce non può toccare l’archiviazione. Le route di caricamento ripristinabile TUS contano come write a ogni passaggio (incluso il controllo dell’offset e l’annullamento), così una chiave con ambito di scrittura può completare un caricamento da sola.

Un agente ha bisogno della chiave più ristretta che svolga il suo compito, non di una chiave admin. Parti da un ambito ristretto e dalle una scadenza:

rebase api-keys create -n "My Agent" \
--permissions '[{"collection":"articles","operations":["read"]}]' \
--expires 30d

Le operazioni sono read, write e delete, derivate dal metodo HTTP: GET/HEAD/OPTIONSread, POST/PUT/PATCHwrite, DELETEdelete.

Una chiave con ambito ristretto legge zero righe finché una regola non concede service

Sezione intitolata “Una chiave con ambito ristretto legge zero righe finché una regola non concede service”

È il passaggio che fa sembrare rotta una chiave correttamente ristretta. Una chiave non-admin viene eseguita come uid: "api-key:<id>" con i ruoli ["service"], e la politica RLS iniettata per impostazione predefinita in ogni collezione viene compilata in:

auth.uid() IS NULL OR (string_to_array(auth.roles(), ',') && ARRAY['admin'])

— il contesto server, oppure un admin. Una chiave non-admin non corrisponde a nessuno dei due rami, quindi su una collezione senza securityRules la richiesta va a buon fine con un insieme di risultati vuoto e senza alcun errore che ne spieghi il motivo. Concedi il ruolo esplicitamente:

securityRules: [
{ operation: "select", roles: ["service"], using: "true" }
]

Poiché auth.uid() contiene l’id della chiave, una regola può anche restringere le righe a una chiave specifica:

securityRules: [
{
operation: "select",
condition: policy.compare(policy.authUid(), "eq", policy.literal("api-key:<id>"))
}
]

Il carattere jolly "*" non copre solo le collezioni — corrisponde anche al namespace functions e a storage. Una GET conta come read, e l’handler di una funzione personalizzata è codice arbitrario che può scrivere: una chiave jolly apparentemente in sola lettura può quindi modificare i dati attraverso una funzione. Nominare le collezioni esplicitamente non lascia alla chiave alcun accesso alle funzioni.

--admin --full-access: CI, migrazioni e strumenti interni

Sezione intitolata “--admin --full-access: CI, migrazioni e strumenti interni”

"admin": true concede alla chiave il ruolo admin — le route /api/admin/* per la gestione dello schema, la gestione degli utenti e altro ancora, più cron, backup e log. Combinata con --full-access ({"collection": "*", "operations": ["read", "write", "delete"]}), la chiave detiene ogni collezione, più tutta l’archiviazione e ogni funzione personalizzata. È la forma giusta per la CI, le migrazioni e gli strumenti interni fidati — non per gli agenti.

# CLI
rebase api-keys create -n "CI" --admin --full-access
# REST
curl -X POST http://localhost:3000/api/admin/api-keys \
-H "Authorization: Bearer <service-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "CI",
"admin": true,
"permissions": [{ "collection": "*", "operations": ["read", "write", "delete"] }]
}'

Il WebSocket in tempo reale non interpreta i token rk_ — accetta soltanto i JWT utente e la chiave di servizio. Un agente autenticato con una chiave API fa polling sugli endpoint REST invece di sottoscriversi.

Campo Tipo Descrizione
name string Etichetta leggibile dall’uomo
permissions ApiKeyPermission[] Accesso per collezione ("*" = tutto; "functions/<name>" = una funzione; "storage" = archiviazione file)
admin boolean Concedere il ruolo admin — route admin + politiche admin RLS
rate_limit number | null Richieste per finestra di 15 min (null = il valore predefinito del server, 1000)
expires_at string | null Timestamp di scadenza ISO-8601

La CLI richiede un ambito esplicito: passa --permissions '<json>' oppure scegli --full-access — non esiste un valore predefinito silenzioso di accesso completo.

Le chiavi possono essere elencate, aggiornate e revocate tramite /api/admin/api-keys o i comandi CLI rebase api-keys — ma non da una chiave API. Qualsiasi richiesta a /api/admin/api-keys autenticata con una chiave rk_ viene rifiutata con 403 API_KEY_SELF_MANAGEMENT_FORBIDDEN, qualunque sia il suo flag admin. La gestione delle chiavi richiede la sessione di un utente admin, oppure la chiave di servizio.

Ottieni un elenco di tutte le collezioni disponibili e della loro struttura:

GET /api/collections