Inside our multi-tenant architecture — one DB, isolated tenants
How we run SmartRestro for 50+ restaurants on one MongoDB cluster without leaking a single order across tenants.
By Osama Khan
The trap: per-tenant databases
Lots of SaaS teams default to "one database per customer". It feels safe. It's actually a nightmare:
- Migrations multiply (you have to run them N times)
- Backups multiply
- Connection pool exhaustion at scale
- Onboarding a new customer takes minutes instead of milliseconds
We chose the harder-up-front, easier-forever path: **one MongoDB cluster, every document tagged with a tenant ID, server-side enforcement on every query.**
The pattern
Every collection has a `restaurant: ObjectId` field. Every query goes through a `branchScope(req)` helper:
```js function branchScope(req) { if (!req.user) return { _denyAll: true }; const role = req.user.role; const branchId = req.user.branchId; if (OWNERS.includes(role)) return {}; // owner sees all branches if (branchId) return { branch: String(branchId) }; return { _denyAll: true }; } ```
This gets spread into every Mongo query: `Order.find({ ...branchScope(req), restaurant: req.restaurant._id })`.
Why server-side, not client-side
Some teams enforce this in the frontend ("hide it in the UI"). That's not security — that's obscurity. A determined user opens DevTools and queries directly. Server-side is the only line that matters.
How we prevent forgetting to spread the scope
Every new feature reviewer's first question is "where's the branchScope?" Code review catches 95% of misses. The remaining 5% is caught by an integration test that creates two restaurants and verifies their data doesn't cross-pollute.
Backups + indexes
- Daily full snapshots at MongoDB Atlas, retained 7 days
- Indexes optimized for the most common access pattern: `{ restaurant: 1, branch: 1, createdAt: -1 }`
- Hot path queries run < 50ms p95
What we'd do differently
At 1,000+ tenants, we'll likely shard by region (Pakistan, MENA, etc.). Right now at <100, one cluster is overkill — but we built it that way from day one because the migration to sharding is much easier than the migration from per-tenant databases.
The lesson
Pick the architecture that scales to 100× your current size, not 1,000×. Premature scaling is its own form of debt.