Most multi-tenant Flask tutorials get it wrong
Most multi-tenant Flask tutorials get the data model wrong. They tell
you to "add a tenant_id column" and call it done — without
ever showing you the patterns that prevent cross-tenant data leaks at
scale.
This scaffold is the boring, correct version: enforced query scoping at the model layer, tested end-to-end, with explicit escape hatches for admin-only views. Every line exists because its absence has burned somebody.
The five things most tutorials miss:
- The
for_tenant()query helper that throws if you forget the tenant_id. Most tutorials let you writedb.query(Model).all()and discover the leak in production. - A clearly named escape hatch
(
all_tenants_unsafe()) for super-admin views — so code review can flag every cross-tenant query. - Stripe webhook idempotency. Stripe replays events. If your handler isn't idempotent, you'll double-charge customers.
-
client_reference_idon the Checkout session to map the payment back to the tenant. Without it, you can't reliably activate the right subscription. - Tests that prove the multi-tenancy guarantee. Not "looks correct" — actually log in as one tenant and verify HTTP 404 when querying another tenant's resource by ID.
What's in the box
- Multi-tenant data model with enforced query scoping — tenant A literally cannot see tenant B's data, verified by tests.
- Email + password signup / login with bcrypt.
- Stripe Checkout integration with idempotent webhook handlers (test mode by default, flip to live in 5 minutes).
- Stripe Customer Portal for self-serve subscription management.
- Super-admin dashboard for support overrides — cancel, reinstate, comp customers onto Pro.
- JSON API + server-rendered UI with Tailwind. No build step in dev.
- Alembic migrations — schema versioning works.
- Postgres in production, SQLite for local dev — zero setup.
- Procfile + railway.toml + Dockerfile — deploy in one push.
- Pre-commit hooks with gitleaks, ruff, debug-statement detection.
- 28 tests covering signup, login, multi-tenancy isolation, Stripe webhook handling, admin role enforcement, and the full signup → checkout → active flow.
Who this is for
This scaffold is for you if:
- You're an indie founder building your first SaaS who doesn't want to spend the first 3 days re-deriving multi-tenancy basics.
- You're an engineer leaving a big-co job and starting solo who knows what good looks like but doesn't want to set it up from scratch.
- You've been burned by a leaky multi-tenant app and want the enforce-at-the-data-layer version next time.
This scaffold is NOT for you if:
- You need OAuth-only auth, magic-link auth, or SSO out of the box — the scaffold is bcrypt + sessions; layer your own on top.
- You're already on Django (you have your own conventions).
- You're expecting a no-code admin like Forest or Retool — this is a Flask blueprint, not a CMS.