Gerex Food
Multi-tenant restaurant management SaaS
A multi-tenant SaaS for restaurants: stock, orders, finances, alerts and realtime reporting, with distinct interfaces for front of house and kitchen. A single API serves both the restaurant dashboard and the platform back office.
- 0
- rows served without a tenant
- 3
- applications
Architect & developer — multi-tenant API, dashboard, superadmin
In multi-tenant SaaS, cross-tenant data leaks are never a decision — they are an oversight. One query written without the `tenant_id` filter, in a report or an export added six months later, and one restaurant sees another's revenue. Relying on every developer's discipline on every query is a strategy that always fails eventually.
I made isolation structural rather than declarative. Middleware resolves the current tenant on every request, a request-scoped singleton carries it, and a trait applies a global scope to every tenant-owned model while auto-stamping `tenant_id` on create. The developer has nothing to remember.
The key part is what happens when no tenant resolves: the scope does not open access, it closes it. With no resolvable tenant, queries return zero rows. A resolution bug produces an empty page — visible and fixable — instead of a silent dump of every tenant's data.
An escape hatch exists, but it is explicit: the cross-tenant analytics layer has to drop global scopes by hand and filter on its own. Making the dangerous case verbose and the safe case automatic is the entire point.
- 01
Tenant resolution
Middleware appended to the `api` group reads the `X-Tenant-Slug` header (or route param), falling back to the authenticated user's `tenant_id`.
TenantMiddleware
- 02
Fail-safe scope
A global scope that returns zero rows when no tenant resolves. Failure closes by default rather than opening.
TenantScope
- 03
Queues & realtime
Laravel Horizon on Redis for background processing, and Laravel Reverb to push live orders and alerts to front of house and kitchen.
Horizon · Reverb
- 04
Restaurant dashboard
Next.js 16, the teams' day-to-day app: stock, orders, finances, alerts.
Next.js 16
- 05
Platform superadmin
Separate app for supervising every tenant, with cross-tenant analytics explicitly unscoped.
Next.js 16
The scope closes rather than opens
The naive implementation of a multi-tenant scope filters nothing when the tenant is missing — and therefore serves everything. I inverted the default: no tenant, no rows. The single exception is the user model, which must stay findable so authentication can work before any tenant is known.
- Tenant isolation applied automatically by global scope, failing closed by default
- Stock, orders, finances and realtime reporting in a single application
- Distinct interfaces for front-of-house and kitchen teams
- Background processing under Horizon, realtime over Reverb WebSockets
- One API serving both the tenant dashboard and the platform superadmin