Le backend,
à lui seul.
REST, a typed SDK, realtime over WebSocket, auth, storage and an OpenAPI spec — with every access rule enforced by the database itself. Point it at your own Postgres, or run rebase dev and get one without installing anything. No admin panel required.
REST, sans l'écrire
Chaque table que vous exposez devient un endpoint
No controllers, no serializers, no validation middleware. Filtering, sorting, pagination and relation loading are query parameters, in a PostgREST-compatible syntax your team probably already knows.
Nested relations are addressable as paths, so a customer's orders are a URL rather than a join you hand-wrote.
GET /api/data/orders?status=eq.paid&total=gt.100
GET /api/data/orders?or=(status.eq.paid,total.gt.500)
GET /api/data/orders/42?include=customer,items
GET /api/data/customers/7/orders
POST /api/data/orders
PUT /api/data/orders/42
DEL /api/data/orders/42Le client typé
De l'autocomplétion qui vient de votre schéma
rebase generate-sdk transforme vos collections en un type Database. Passez-le au client et les noms de collection, les noms de champ, les opérateurs de filtre et les formes de retour sont tous vérifiés à la compilation — y compris les champs demandés dans include.
Renommez une colonne dans la collection et les points d'appel passent au rouge avant toute livraison. Le dépôt qui consomme l'API n'a pas besoin d'être celui qui la définit : --from <url> génère le même client depuis un backend en cours d'exécution, dans un projet sans schéma propre. Votre application web, votre panneau et votre client mobile restent chacun dans leur dépôt, et la CI repère un client périmé avant un utilisateur.
// generated once by `rebase generate-sdk`
import type { Database } from "./generated/sdk/database.types";
const client = createRebaseClient<Database>({
baseUrl: "https://api.example.com",
});
// collection names, field names and return types all autocomplete
const { data } = await client.data.orders
.find({
filter: { status: ["eq", "paid"] },
include: ["customer"],
limit: 50,
});Realtime
Les changements arrivent, quelle qu'en soit l'origine
listen() sur n'importe quelle collection et chaque insertion, mise à jour et suppression arrive au client par WebSocket. Les abonnements respectent la même sécurité au niveau des lignes qu'une lecture : on ne pousse jamais à un abonné une ligne qu'il n'aurait pas pu récupérer.
Les écritures faites hors de l'API comptent aussi — une ligne modifiée depuis psql, un cron ou l'éditeur SQL de Studio émet le même événement, car la capture de changement se fait dans la base. Les canaux de diffusion et la présence sont par instance par défaut ; réglez realtime.bus sur { type: "postgres" } et ils se propagent entre répliques via LISTEN/NOTIFY, ou fournissez votre propre transport.
- RESTPATCH /api/data/orders/42your app
- psqlUPDATE orders SET status = 'shipped' WHERE id = 42;a person at a terminal
- cronUPDATE orders SET status = 'closed' WHERE …a scheduled job
- StudioUPDATE orders … — run from the SQL editorthe admin panel
→ one change event: orders · UPDATE · id 42
→ each subscriber re-reads the row under its own access rules
Authorization
Les règles vivent dans Postgres, pas dans ce serveur
Les requêtes s'exécutent sous un rôle de base restreint. Vos securityRules se compilent en véritables politiques de sécurité au niveau des lignes : une requête qui ne devrait rien renvoyer ne renvoie rien — qu'elle arrive par le SDK, par REST, par le panneau d'administration, par la clé d'API d'un agent ou par une session psql. Une collection sans politique ne sert aucune ligne : par défaut c'est fermé, pas ouvert.
export const orders = {
slug: "orders",
table: "orders",
securityRules: [
// customers see their own orders…
{ operation: "select", ownerField: "customer_id" },
// …support can read and update all of them
{ operations: ["select", "update"],
roles: ["support"] },
],
};-- rebase schema generate → db push
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY orders_select_1f4c9ab ON orders
FOR SELECT TO rebase_user
USING (customer_id = rebase.uid());
CREATE POLICY orders_update_8b02e13 ON orders
FOR UPDATE TO rebase_user
USING (string_to_array(rebase.roles(), ',')
&& ARRAY['support']);
-- no policy for INSERT or DELETE:
-- nobody can insert or delete. Closed by default.npx @rebasepro/rls-check lit DATABASE_URL et l'audite sans rien installer.Un serveur
Tout est monté, rien à assembler
Voici la table de routage d'un projet par défaut — un processus, un déploiement, aucun maillage de services à dessiner. Chacune existe parce que la construire soi-même coûte une semaine que vous préféreriez passer ailleurs.
Un processus est la valeur par défaut, pas le plafond. Le même build démarre aussi en API, en étage de fonctions et en worker — une variable par processus décide quelles routes sont montées, quels minuteurs se déclenchent et lequel possède le schéma.
Topics and jobs. Publish an event, subscribe by name. One durable row per subscriber, each retrying on its own schedule — and a publish inside a transaction that rolls back never happened.
Webhooks. HMAC-signed outbound calls on insert, update and delete, with retries.
Entity history. An audit trail per collection: who changed what, when, and a revert.
Database branching. Isolated copies of the whole database for a feature or a test run — schema, rows and policies — with one command to switch your project onto one and back.
Email. SMTP for verification, password reset and your own transactional templates.
Backups. Role-complete dumps and restores, driven from the CLI or the admin API.
Multiple sources. Route different collections to different databases from one server.
/api/data/:collectionCRUD, filters, sorting, pagination, relation includes, nested subcollections/api/auth/*sign-up, login, refresh, password reset, OAuth, MFA, roles/api/storage/*upload, download, signed access — local, S3-compatible or GCS/api/functions/:nameyour own Hono routes, auto-mounted from functions//api/cron/*scheduled jobs with run history and manual triggers/api/docs · /api/swaggerOpenAPI 3.0 spec, plus an explorer in dev/api/metathe runtime contract: collections, properties, capabilities/healthreadiness for your orchestrator — asserts the auth schema, not just TCPWebSocketrow subscriptions, broadcast channels and presence on the same server
Underneath
De la technologie ennuyeuse, exprès
Une application Hono et un schéma Drizzle. Deux choses que votre équipe peut lire, déboguer et étendre sans apprendre un framework — et sans attendre que nous exposions un hook.
import { initializeRebaseBackend } from "@rebasepro/server";
import { createPostgresBootstrapper } from "@rebasepro/server-postgres";
const backend = await initializeRebaseBackend({
server,
app,
bootstrappers: [
createPostgresBootstrapper({
connection: db,
schema: { tables, enums, relations },
})
],
auth: { jwtSecret: process.env.JWT_SECRET },
storage: { type: "local", basePath: "./uploads" },
});
// it's a Hono app — mount it, wrap it, add your own middleware- Montez-le comme sous-app sur un serveur que vous exploitez déjà
- Vos middlewares, vos routes, votre gestion d'erreurs
- Node, Docker, Railway, Fly.io ou bare metal
// one collection, one relation
const posts = {
slug: "posts",
properties: { title: { type: "string" } },
relations: [{
relationName: "author",
target: () => users,
cardinality: "one-to-one"
}]
};
// GET /api/data/posts?include=author
// → one query via db.query.findMany, not 1 + N- Un-à-un, un-à-plusieurs et plusieurs-à-plusieurs
- Sélection de champs — récupérez les colonnes demandées
- Pagination par curseur et par offset
L'autre moitié
Headless par défaut
Le panneau d'administration est un client de cette API, pas une couche interne. Il lit à travers les mêmes politiques que votre application. Activez-le ci-dessous : le projet gagne une dépendance et un bloc admin imbriqué, un panneau d'administration apparaît — et la réponse d'API à droite ne bouge pas.
A headless project: no admin block, no panel — just the API.
Your project
Dependencies
- @rebasepro/server
- @rebasepro/server-postgres
- @rebasepro/client
Without admin.d.ts, an `admin` key on a collection is a type error.
config/collections/users.ts
export const users = {
name: "Users",
table: "users",
properties: {
email: { type: "string" },
displayName: { type: "string" },
},
securityRules: [
{ operation: "select",
using: "id = rebase.uid()::uuid" },
], admin: {
icon: "Users",
group: "Settings",
listProperties: ["displayName", "email"],
},};
The server loads this file either way — and never reads inside admin.
Your API
identical[{ "id": "9f2…", "email": "ada@…",
"displayName": "Ada" }]Admin panel
Not installed.
Nothing is served, nothing is bundled.
The only thing that changed is what a human can see.
Pointez-le vers votre base de données.
Échafaudez sur une base Postgres que vous exploitez déjà et lisez les routes REST, le document OpenAPI et les politiques générées.