Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityCopilot reviewers speak out on grading AI-generated explicit content(29.09.2026 um 16:58 Uhr)
••
Unix & Linux ServerFirefox in a New Skin! Nova Redesign Has Become The Default(29.09.2026 um 15:54 Uhr)
••
Sichere ProgrammierungNach dem Start der Googlebooks: Wie geht es mit Chrome OS weiter?(29.09.2026 um 16:30 Uhr)
••••
Sichere ProgrammierungWhy Software Automation Needs a Layer Between Intent and Execution(29.09.2026 um 08:16 Uhr)
••
Windows Tipps & SecurityCopilot reviewers speak out on grading AI-generated explicit content(29.09.2026 um 16:58 Uhr)
••
Unix & Linux ServerFirefox in a New Skin! Nova Redesign Has Become The Default(29.09.2026 um 15:54 Uhr)
••
Sichere ProgrammierungNach dem Start der Googlebooks: Wie geht es mit Chrome OS weiter?(29.09.2026 um 16:30 Uhr)
••••
Sichere ProgrammierungWhy Software Automation Needs a Layer Between Intent and Execution(29.09.2026 um 08:16 Uhr)
••
Intelligence View
⚡ tsecurity.de Intelligence

Multi-Tenancy in Bun/Hono Without Boilerplate

Every multi-tenant SaaS has the same problem: you need to make sure every query only returns data for the right tenant. Forget a WHERE tenant_id = ? once, and you have a data leak. The obvious solution — a separate database per tenant — do…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Every multi-tenant SaaS has the same problem: you need to make sure every query only returns data for the right tenant. Forget a WHERE tenant_id = ? once, and you have a data leak.



The obvious solution — a separate database per tenant — doesn't scale. Connections are expensive, migration overhead multiplies, and you lose cross-tenant reporting.



For Kumiko we went a different route: a single DB pool, but every query automatically gets the tenantId injected — without handler code ever having to do it manually.






The Idea: TenantDb Instead of a Raw DbRunner



Instead of passing a raw DB connection around, we create a TenantDb wrapper per request:




const db = createTenantDb(rawDb, tenantId)






From that point, db behaves like a normal database — but with automatic isolation baked in:




// Handler code — no tenantId needed
const users = await ctx.db.selectMany(usersTable)
// → SELECT * FROM users WHERE tenant_id IN ('tenant-123', 'system')

await ctx.db.insertOne(usersTable, { name: 'Max' })
// → INSERT INTO users (name, tenant_id) VALUES ('Max', 'tenant-123')

await ctx.db.updateMany(usersTable, { name: 'Moritz' }, { id: userId })
// → UPDATE users SET name='Moritz' WHERE id=? AND tenant_id='tenant-123'






Handlers write plain CRUD code. Isolation happens underneath — invisible, but enforced.






How the Injection Works






Reads: own rows + reference data



Read queries always see two tenants: the current one and SYSTEM_TENANT_ID. This allows reference data (e.g. global config) to be visible to all tenants without duplicating it:




// tenantId filter === [currentTenantId, SYSTEM_TENANT_ID]






If a handler passes its own tenantId in the WHERE clause, it can only narrow the scope, never widen it. A where: { tenantId: 'other-tenant' } is silently dropped.






Writes: own rows only



Inserts get tenantId forced in — and the value cannot be overridden by the caller:




// mode === "tenant": tenantId on INSERT is enforced last
return { ...data, tenantId } // overwrites whatever the handler passed






Updates and deletes without a WHERE clause throw an error instead of hitting all rows:




// Prevents accidental mass-updates
"TenantDb.updateMany without where would mass-update all tenant rows."









System mode for operators



For admin screens there's r.systemScope() — queries run unfiltered across all tenants. Explicit opt-in only, never the default.






Tenant Resolution in Hono



TenantDb needs a tenantId. It comes from middleware that resolves it from the request — either from the hostname (for custom domains) or from the JWT:




// Middleware (simplified)
app.use('*', async (c, next) => {
const tenant = await resolveTenant(c.req)
c.set('db', createTenantDb(rawDb, tenant.id))
await next()
})






Every handler then gets proper isolation through ctx.db — without a single line of tenant logic in actual feature code.






What This Means in Practice



Across three years of production use and several apps (CashColt, publicstatus, kumiko-studio) we've had zero data leak bugs from forgotten tenant filters. Not because we were particularly careful, but because it's structurally impossible to forget.



The overhead: nearly zero. A few extra conditions per query, no extra DB connection pool, no migration overhead multiplied per tenant.



If you want to try the framework: kumiko.rocks — open source under BUSL-1.1.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Multi-Tenancy in Bun/Hono Without Boilerplate

Thematisch verwandte Begriffe: MultiTenancy, BunHono, Without, Boilerplate · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-8065 | An authentication bypass vulnerability in the firmware update endpoint of…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag