Tenant DB Context
Prinzip: Ein Weg
ctx.db ist immer tenant-scoped. Der Entwickler bekommt nie ein ungefiltertes DB-Objekt. Es gibt keinen zweiten Weg.
r.writeHandler("order.create", schema, async (event, ctx) => { // ctx.db ist tenant-scoped — einziges DB-Objekt das es gibt await ctx.db.insert(orders).values({ name: "test" }); // → tenantId wird automatisch injiziert
await ctx.db.select().from(orders); // → WHERE tenantId = 42 automatisch
// Es gibt kein ctx.rawDb, kein ctx.unscopedDb // Man KANN nicht am Tenant vorbei});Wie es funktioniert
Pipeline erstellt pro Request ein tenant-scoped DB-Objekt:
// In der Pipeline — einmal pro Request:const tenantDb = createTenantDb(db, user.tenantId);ctx.db = tenantDb;Jede Operation ist automatisch gefiltert:
| Operation | Was passiert automatisch |
|---|---|
ctx.db.select().from(orders) | WHERE tenantId = 42 |
ctx.db.insert(orders).values(data) | tenantId: 42 injiziert |
ctx.db.update(orders).set(data) | WHERE tenantId = 42 |
ctx.db.delete(orders) | WHERE tenantId = 42 |
System-Scope: Tenant-uebergreifende Features
Manche Features brauchen Zugriff auf alle Tenants (System Dashboard, Tenant Management). Diese deklarieren r.systemScope():
defineFeature("systemDashboard", (r) => { r.systemScope(); // ctx.db OHNE Tenant-Filter r.access({ roles: ["SystemAdmin"] }); // Pflicht bei systemScope
r.queryHandler("dashboard.tenantStats", schema, async (query, ctx) => { // ctx.db → kein Tenant-Filter, sieht alle Daten // Entwickler ist selbst verantwortlich fuer korrekte Filterung const stats = await ctx.db.select().from(tenants); });});| Feature-Typ | ctx.db | Wer darf |
|---|---|---|
| Normal (Default) | Tenant-scoped | Per Handler Access |
System (r.systemScope()) | Unscoped | Muss explizit eingeschraenkt werden |
Boot-Validierung
| Pruefung | Fehler |
|---|---|
r.systemScope() ohne r.access() | System-scoped Feature "systemDashboard" requires explicit access restriction |
r.systemScope() + access: { roles: ["Driver"] } | Warning: System-scoped Feature accessible by non-admin role "Driver" |
SYSTEM_USER
Migrations, Tenant-Create, Scheduled Jobs die alle Tenants betreffen — diese laufen mit SYSTEM_USER der keinen Tenant-Filter hat. Das ist nie im Feature-Code sondern im Framework-Core.
Siehe: system-user.md