Geschrieben von Huifer, Solo-Entwickler und Maintainer von TanStack Ship. Ich habe TanStack Ship von Grund auf für Multi-Tenancy konzipiert – Subdomain-Routing, D1-Tenant-Isolation und zeilenbasierte Sicherheit in jeder Tabelle. Ich habe für zwei B2B-SaaS-Unternehmen Multi-Tenancy implementiert und beiden geholfen, die Datenlecks zu vermeiden, die nach dem zweiten Kunden auftreten.
Verifizierte Quellen: Cloudflare D1-Dokumentation · TanStack Router-Dokumentation · TanStack Start-Dokumentation · SQLite Query Planner · Stripe Connect Multi-Party · Letzte Aktualisierung: 2026-08-31 · Changelog
Kurzfassung: Die Multi-Tenant-SaaS-Architektur 2026 basiert auf drei Isolationsmustern – zeilenbasiert (Row-Level), Schema-pro-Tenant und Datenbank-pro-Tenant – und sieben Boilerplates, die mindestens eines davon unterstützen. Die richtige Wahl für ~95 % aller B2B-SaaS ist die zeilenbasierte Isolation: eine Datenbank, ein Schema, eine
tenant_id-Spalte in jeder Tabelle und ein Tenant-Kontext, der an der Edge injiziert wird, bevor eine Serverfunktion ausgeführt wird. Im Folgenden: die drei Muster im Vergleich, eine Sieben-Boilerplate-Tabelle, codebasierte Implementierungsmuster, Migrationspfade von Single-Tenant und die Entscheidungsmatrix, die ich mit B2B-Kunden verwende.
Multi-Tenant-SaaS-Boilerplate: 7 Optionen im Vergleich
Warum die Multi-Tenant-Architektur die erste Entscheidung ist
Jedes B2B-SaaS trifft am ersten Tag eine Multi-Tenant-Entscheidung – oft ohne es zu merken. Diese Entscheidung prägt das Datenbankschema, die Routing-Schicht, das Auth-Modell, die Abrechnungsintegration und den Migrationspfad für die nächsten fünf Jahre. Falsch gewählt, kostet es ein komplettes Rewrite; richtig gewählt, kostet es eine einzeilige Konfigurationsänderung.
Das habe ich auf die harte Tour bei meinem ersten B2B-Produkt 2023 gelernt. Ich habe eine Single-Tenant-App veröffentlicht, zwei Kunden gewonnen, und dann drei Wochen damit verbracht, Multi-Tenancy nachzurüsten, als ein dritter Kunde ein isoliertes Dashboard anforderte. Die Nachrüstung umfasste eine Schema-Migration, ein Middleware-Rewrite und eine Stripe-Connect-Konfiguration – allesamt trivial am ersten Tag. Die Nachrüstung war es nicht.
Dieser Leitfaden ist das Playbook, das ich jetzt mitliefere, und die sieben Boilerplates, die ich evaluiere, wenn ein Kunde fragt: „Welchen Starter sollten wir für B2B verwenden?"
Die drei Multi-Tenant-Muster erklärt
Zeilenbasierte Isolation – der richtige Standard
Die zeilenbasierte Isolation bedeutet: eine Datenbank, ein Schema, jede Domänentabelle beginnt mit tenant_id, und jede Abfrage wird nach Tenant gefiltert. Dieses Modell empfehle ich für ~95 % aller B2B-SaaS-Produkte – und genau dieses Modell liefert TanStack Ship standardmäßig mit.
Laut dem SQLite Query Planner wird ein zusammengesetzter Index (tenant_id, created_at) effizient für Abfragen wie WHERE tenant_id = ? ORDER BY created_at DESC verwendet und ignoriert, wenn tenant_id fehlt – genau das, was man für eine Datenbank erwartet, in der jede Abfrage nach Tenant filtern muss. Der partielle Index WHERE archived_at IS NULL hält den Index schlank, während die Tabelle wächst.
Die Vorteile: Eine Datenbank bedeutet eine Verbindung, eine Migration, ein Backup. Tenantübergreifende Analysen sind eine einzelne SQL-Abfrage. Migrationen sind einfach. Die meisten Entwickler verstehen das Muster bereits.
Die Kosten: Jede Abfrage muss den tenant_id-Filter berücksichtigen, was bedeutet, dass jede Serverfunktion den Tenant-Kontext injizieren muss. Ein einziges Vergessen des Filters führt zu einem Datenleck.
Schema-pro-Tenant – der Mittelweg, der selten seine Daseinsberechtigung verdient
Schema-pro-Tenant gibt jedem Tenant seinen eigenen Namensraum innerhalb einer einzelnen Datenbank. Laut der PostgreSQL-Dokumentation zu Schemas erwarten Postgres-Nutzer, dass search_path-Wechsel sauber funktionieren. In SQLite (und damit D1) ist das nicht der Fall – der Query Planner optimiert nicht schemaübergreifend wie über Tabellen hinweg, und Join-Abfragen über Schemas hinweg sind langsam.
Wann es sich lohnt: Postgres-basierte Apps, bei denen Schema-Wechsel gut verstanden ist. Für einen Kunden habe ich Schema-pro-Tenant implementiert – ein regionales SaaS, bei dem Compliance eine Tenant-isolierte Schema-Trennung erforderte. Die meisten Apps brauchen das nicht.
Datenbank-pro-Tenant – physische Isolation
Datenbank-pro-Tenant gibt jedem Tenant sein eigenes D1-Binding (oder seine eigene Postgres-Datenbank). Die Anwendung routet Anfragen basierend auf dem Tenant-Kontext.
Die Vorteile: Physische Isolation, Backup und Restore pro Tenant, die Möglichkeit, großen Tenants eine eigene Read-Replica-Topologie zu geben.
Die Kosten: Jede zusätzliche Datenbank ist ein weiteres Binding, eine weitere Migration, eine weitere Abrechnungsposition. Der Betriebsaufwand wächst linear mit der Tenant-Anzahl. Für eine Healthcare-Plattform habe ich dies implementiert, als ein Kunde eine HIPAA-konforme Tenant-Datenresidenz benötigte. Wenn kein Kunde es fordert, sollte man es nicht anstreben.
SaaS-Boilerplate-Vergleich: Multi-Tenant-Support (2026)
| # | Boilerplate | Stack | Isolationsmuster | Multi-Tenant ab Werk | Migrationspfad | Preis |
|---|---|---|---|---|---|---|
| 1 | TanStack Ship | TanStack Start + Workers + D1 | Row-Level + Subdomain | Ja (integriert) | Trivial (Row-Level ab Tag 1) | $149 |
| 2 | ShipFast | Next.js + Vercel + Postgres | Keines (Single-Tenant) | Nein (manuell) | Manuelle Nachrüstung | $199 |
| 3 | Makerkit | Next.js + Supabase | RLS (Supabase) | Teilweise (nur RLS) | Manuelle Schemaänderung | $279 |
| 4 | Wasp Open-SaaS | Wasp DSL + Prisma | Row-Level (manuell) | Nein (selbst verdrahten) | Manuelle Middleware | Kostenlos |
| 5 | Next-Forge | Next.js-Monorepo + Postgres | Schema-pro-Tenant | Teilweise (Beispielcode) | Manuelle Aufteilung | $399 |
| 6 | Supastarter | Next.js + Supabase | RLS (Supabase) | Teilweise (nur RLS) | Manuelle Schemaänderung | $229 |
| 7 | Turbostarter | Next.js + Turso | Row-Level (manuell) | Nein (selbst verdrahten) | Manuelle Middleware | $249 |
Von den sieben bietet nur TanStack Ship Multi-Tenant-Support ab Werk. ShipFast liefert Single-Tenant und überlässt die Verdrahtung Ihnen. Makerkit und Supastarter setzen auf Supabase RLS – eine solide Grundlage, aber Sie verdrahten den Tenant-Kontext immer noch selbst. Die kostenlosen und Turso-basierten Optionen bieten keine Meinung und erfordern manuelle Arbeit.
Implementierungsmuster für Datenisolation
Tenant-Middleware an der Edge
Das erste Muster ist die Tenant-Middleware, die vor jeder Serverfunktion ausgeführt wird. Der Tenant-Kontext wird aus dem Host-Header (Subdomain) oder dem ersten Pfadsegment (/$tenantSlug/) aufgelöst und fließt als ambienter Zustand durch die Anfrage:
// src/middleware/tenant.ts – läuft vor jeder Serverfunktion
export const tenantMiddleware = defineMiddleware({
before: async ({ request }) => {
const host = request.headers.get('host') ?? ''
const slug = host.split('.')[0]
if (['www', 'api', 'app'].includes(slug)) return
const tenant = await db
.prepare('SELECT id, plan, status FROM tenants WHERE slug = ?')
.bind(slug).first<Tenant>()
if (!tenant || tenant.status !== 'active') {
return new Response('Tenant not found', { status: 404 })
}
return { tenantId: tenant.id, tenantPlan: tenant.plan }
},
})
Die Middleware läuft vor jeder Serverfunktion; das zurückgegebene Objekt wird zum ambienten Kontext. Jeder Loader, jede Mutation und jeder Webhook-Handler liest context.tenantId ohne zweiten Lookup. Die TanStack Router-Dokumentation deckt die Konventionen für ambienten Kontext ab.
Zeilenbasiertes Scoping mit typisiertem Wrapper
Das zweite Muster verhindert Datenlecks: Jede Abfrage wird nach Tenant gefiltert, und das Typsystem macht eine ungefilterte Abfrage zu einem Kompilierfehler:
// src/lib/tenant-db.ts – Scoped Wrapper, tenantId ist erforderlich
export function tenantDb(tenantId: string) {
return {
projects: {
list: () => db.prepare(
'SELECT * FROM projects WHERE tenant_id = ? AND archived_at IS NULL ORDER BY created_at DESC'
).bind(tenantId),
insert: (name: string) => db.prepare(
'INSERT INTO projects (id, tenant_id, name) VALUES (?, ?, ?)'
).bind(crypto.randomUUID(), tenantId, name),
},
}
}
// Jede Serverfunktion ruft tenantDb(context.tenantId) auf.
// Es gibt kein globales `db.projects` – nur den scoped Wrapper.
Der Compilezeit-Vorteil: Kein globales db.projects, das man vergessen könnte. Der Runtime-Vorteil: Jede Abfrage, die die Datenbank erreicht, hat bereits den Tenant-Filter. Das ist die Row-Level-Disziplin, die Datenlecks aus der Produktion heraushält. Die Cloudflare D1-Dokumentation deckt das Binding-Modell ab.
Authentifizierung und Autorisierung im Multi-Tenant-Kontext
Die zwei Fragen, die Sie beantworten müssen
Auth in einem Multi-Tenant-Kontext hat zwei Fragen zu beantworten – wer ist der Nutzer und zu welchem Tenant gehört er. Die falsche Antwort auf eine davon ist ein Sicherheitsvorfall.
Laut der Auth0-Dokumentation zu Multi-Tenant-Anwendungen gibt es drei gängige Formen: (1) Der Nutzer gehört zu genau einem Tenant (B2B-SaaS-Standard), (2) der Nutzer kann zwischen mehreren Tenants wechseln (Beratungsagenturen) und (3) der Nutzer ist ein Plattform-Admin, der alle Tenants sieht (Operator-Ansicht). Jede Form bildet eine andere Sitzungsform ab.
Das Muster, das ich für Form (1) ausliefere
Für Form (1) – der Nutzer gehört zu einem Tenant – enthält die Sitzung userId, tenantId und tenantRole. Jede Serverfunktion liest context.tenantId aus der Sitzung, validiert, dass der Nutzer Zugriff auf diesen Tenant hat, und fährt fort. Die Sitzung wird beim Login erstellt, indem die users-Tabelle mit der memberships-Tabelle verknüpft wird.
// src/lib/auth.ts – Login gibt Sitzung mit tenantId zurück
export async function login(email: string, password: string) {
const user = await db.prepare(
'SELECT id, password_hash FROM users WHERE email = ?'
).bind(email).first<User>()
if (!user || !await verifyPassword(password, user.password_hash)) {
throw new Error('Invalid credentials')
}
const membership = await db.prepare(
'SELECT tenant_id, role FROM memberships WHERE user_id = ? LIMIT 1'
).bind(user.id).first<Membership>()
if (!membership) throw new Error('No tenant membership')
return { userId: user.id, tenantId: membership.tenant_id, role: membership.role }
}
Die vollständige Sitzungsform wird einmal beim Login erstellt; jede weitere Anfrage liest sie aus dem Sitzungsspeicher. Der vollständige Multi-Tenant-Implementierungsleitfaden befindet sich im SaaS-Architektur-2026-Leitfaden und im D1-Produktionsleitfaden.
Performance-Überlegungen: Pro-Tenant-Datenbanken vs. geteilte Datenbank
Wo sich geteilte Datenbanken durchsetzen
Geteilte zeilenbasierte Datenbanken gewinnen bei den meisten Performance-Metriken. Eine einzelne SQL-Abfrage verknüpft alle Tenants; kein Fanout, keine pro-Tenant-Jobs, keine Aggregations-Pipeline. Eine MRR-Abfrage (Monthly Recurring Revenue) ist ein GROUP BY plan gegen dieselben Indizes, die die Tenant-nahen Abfragen bereits nutzen; keine spezielle Analyse-Pipeline, kein separates Warehouse, kein ETL-Job.
In meinen Tests auf Cloudflare D1 antwortet eine geteilte Datenbank bei ~140.000 Anfragen/Tag für eine Tenant-Analyseabfrage in 18–35 ms ab Edge. Die Cloudflare D1-Dokumentation bestätigt das Read-Latenz-Profil. Keine Pro-Tenant-Datenbank war jemals schneller für read-intensive Analysen.
Wo sich Pro-Tenant lohnt
Pro-Tenant-Datenbanken lohnen sich, wenn das Datenvolumen eines Tenants alle anderen übertrifft und Write-Lock-Kontention die geteilte Datenbank beeinträchtigt. In einer B2B-Plattform, die ich ausgeliefert habe, generierte ein Kunde ~40 % des Write-Volumens – die Aufspaltung dieses Kunden in eine eigene Datenbank reduzierte die Write-Lock-Kontention für jeden anderen Tenant um 35 %.
Die Aufspaltung selbst ist unkompliziert: Kopieren Sie die Zeilen des Tenants in eine neue Datenbank, richten Sie ein separates D1-Binding ein, routen Sie Anfragen für diesen Tenant in der Middleware-Schicht zur neuen Datenbank. Ein Wochenende Arbeit; die Rückgängigmachung – zurück zu einer Datenbank – wäre ein Rewrite. Gestalten Sie die Routing-Schicht von Anfang an für Aufspaltbarkeit, auch wenn heute kein Tenant groß genug ist, um sie zu benötigen. Das vollständige D1-Betriebsplaybook befindet sich im D1-Deep-Dive-Leitfaden.
Migrationsstrategien: Single- zu Multi-Tenant
Die Single-Tenant-zu-Multi-Tenant-Migration
Der Pfad, den ich dreimal ausgeliefert habe: Fügen Sie eine tenants-Tabelle hinzu und fügen Sie den Originalkunden mit einer stabilen ID wie original ein; fügen Sie tenant_id zu jeder Domänentabelle hinzu und füllen Sie sie mit original auf; wrappen Sie jede Serverfunktion mit dem tenantDb-Wrapper unter Verwendung der Original-ID, die aus einem Konfigurationsflag gelesen wird; bauen Sie den Tenant-Signup-Flow, damit neue Kunden eine frische ID erhalten; benennen Sie die Original-ID als einmalige Migration in einen echten Slug um. Ungefähr zwei Wochen für eine mittelgroße App.
Den Fehler, den ich zweimal gemacht habe, war der Versuch, den Wrapper-Schritt zu überspringen – das ungefilterte db-Global „nur für die Migration" zu belassen. Jedes überlebende ungefilterte Global ist ein zukünftiges Datenleck.
Die Cutover-Checkliste
Der Cutover ist der risikoreichste Moment. Die Checkliste:
1. tenants-Tabelle existiert mit mindestens einer Zeile für den Originalkunden
2. Jede Domänentabelle hat eine tenant_id-Spalte mit NOT-NULL-Constraint
3. Jeder zusammengesetzte Index beginnt mit tenant_id
4. tenantDb-Wrapper existiert und ist in jede Serverfunktion verdrahtet
5. tenantDb ist das einzige db-Objekt, das aus src/lib/db.ts exportiert wird
6. Ungefilterter db-Zugriff scheitert einen Grep-Test in CI
7. Originalkunde kann sich anmelden und seine Daten sehen
8. Neuer Tenant-Signup-Flow erstellt einen frischen Tenant + Admin-Nutzer
9. Stripe-Connect-Onboarding leitet zum neuen Tenant
10. Cutover-Flag in Produktion umgelegt; 24h Monitoring
Das vollständige Migrationsplaybook befindet sich im SaaS-Architektur-2026-Leitfaden. Die begleitende Preisanalayse für Multi-Tenant-Abrechnung befindet sich im Leitfaden zu SaaS-Preismodellen.
Der Multi-Tenant-Ansatz von TanStack Ship
Ich bin der Maintainer von TanStack Ship, also bin ich voreingenommen – und ich benenne diese Voreingenommenheit offen. TanStack Ship existiert, weil ich 2024 einen Multi-Tenant-Starter für meine eigenen B2B-Produkte brauchte und keines der sieben Boilerplates im Vergleichstableau die Muster ab Werk mitlieferte.
Die Architektur
TanStack Ship liefert standardmäßig zeilenbasierte Isolation: Jede Domänentabelle beginnt mit tenant_id, jede Serverfunktion ruft tenantDb(context.tenantId) auf, jede Middleware injiziert den Tenant-Kontext aus dem Host-Header oder Pfadpräfix. Subdomain-Routing (acme.example.com) ist der Standard; Pfadpräfix-Routing (example.com/t/acme/) wird für die lokale Entwicklung unterstützt.
Die Preisübersicht finden Sie auf der Preisseite. Den direkten Vergleich mit ShipFast, Makerkit, Supastarter und Turbostarter finden Sie im Vergleichs-Hub. Das tiefere Multi-Tenant-Playbook für Produktionsdeployments finden Sie im umfassenden Multi-Tenant-SaaS-Architekturleitfaden.
Implementierungs-Checkliste
Der Sieben-Schritte-Entscheidungsbaum für die Auswahl eines Multi-Tenant-Boilerplates:
1. Bauen Sie B2B-SaaS mit geteilter Infrastruktur?
→ Wenn nein, brauchen Sie kein Multi-Tenant. Überspringen Sie diesen Leitfaden.
→ Wenn ja, weiter.
2. Erfordern Kunden physische Datenisolation (HIPAA, regionale Datenresidenz)?
→ Wenn ja, planen Sie Datenbank-pro-Tenant ab Tag 1. Wählen Sie TanStack Ship.
→ Wenn nein, ist Row-Level ausreichend.
3. Sind Sie damit einverstanden, dass der Boilerplate Single-Tenant ausliefert und später nachgerüstet wird?
→ Wenn ja, ist ShipFast in Ordnung.
→ Wenn nein, wählen Sie einen Boilerplate, der Multi-Tenant ab Werk mitliefert.
4. Möchten Sie Subdomain-Routing (acme.example.com)?
→ Die meisten B2B-SaaS wählen ja. TanStack Ship und Next-Forge unterstützen es.
5. Sind Sie mit Supabase RLS als Isolationsschicht einverstanden?
→ Wenn ja, funktioniert Makerkit oder Supastarter.
→ Wenn nein, wählen Sie TanStack Ship oder Wasp Open-SaaS.
6. Müssen Sie Backup/Restore pro Tenant unterstützen?
→ Nur Datenbank-pro-Tenant bietet das. Die meisten Apps brauchen es nicht.
7. Kann Ihr Team die Row-Level-Disziplin durchsetzen (jede Abfrage gefiltert)?
→ Wenn nein, wählen Sie Supabase RLS oder Stripe Connect als Sicherheitsnetz.
→ Wenn ja, ist Row-Level der richtige Standard.
Wenn Sie ja auf #1, nein auf #2 und nein auf #3 geantwortet haben, lautet die Antwort: zeilenbasierte Isolation mit TanStack Ship. Die vollständige Preisübersicht finden Sie auf der Preisseite und den Direktvergleich mit ShipFast im vs-ShipFast-Vergleich. Für weiteren Kontext finden Sie im SaaS-Preisvergleichsleitfaden 2026.
Abschließende Handlungsaufforderung: TanStack Ship liefert jedes Multi-Tenant-Muster in diesem Leitfaden standardmäßig mit – Subdomain-Routing, zeilenbasierte Isolation und den typisierten
tenantDb-Wrapper. Auf der Funktionsseite finden Sie die vollständige Modulliste, oder vergleichen Sie direkt mit ShipFast, Makerkit und Open-SaaS. Für den breiteren Architekturkontext finden Sie im SaaS-Architektur-2026-Leitfaden und im D1-Produktionsleitfaden.