SaaS platform · 2026

A multi-tenant retail ERP and POS SaaS for small shops

One codebase, many shops, zero data leakage: tenant isolation enforced at the ORM layer, permissions instead of roles, tokens that can't be forged into another store.

Client code is private. This describes the engineering
Retail ERP / POS SaaS, SaaS platform
Client
Confidential (Middle East retail)
Industry
Retail / point of sale
My role
Architect and lead engineer, API and web app
Year
2026

Overview

A multi-tenant ERP and point-of-sale SaaS for small and medium shops: products, stock, sales, staff and reporting per organisation, with an owner / manager / cashier permission model, all on a NestJS 11 + Prisma 7 + PostgreSQL 16 API and a Next.js 16 web app.

The problem

Multi-tenancy is where SaaS products quietly leak data. A single missed `where organizationId = …` in one of hundreds of queries and one shop can read another's sales. The system had to make that class of bug impossible rather than merely unlikely, while staying fast enough for a cashier at a till.

What I built

  • Tenant identity carried only in the access token. organizationId is never read from a request body, header or query string.
  • A `TenantGuard` that re-resolves the membership from the database on every request, so a deactivated employee or a suspended shop loses access immediately, not at token expiry.
  • Permission-based guards (not role-based), backed by a permission catalogue, so roles can change without touching code.
  • A Prisma client extension that refuses any query against a tenant-owned table that does not filter by organizationId, so a forgotten filter fails loudly in development instead of leaking in production.
  • Argon2id passwords; refresh tokens stored as SHA-256 digests, rotated on use, with reuse revoking the whole token family.
  • One response envelope, stable error codes, Swagger in non-production, rate limiting, scheduled jobs, S3 uploads, Redis caching, e2e tests against a dedicated database.

Outcome

0

Ways to query a tenant table without a tenant filter

3

Seeded roles (owner, manager, cashier) on one permission catalogue

e2e

Tests against a real Postgres, not mocks

6

Architecture, database, API, permissions, security and local-dev docs

Engineering decisions

  • Tenant isolation as a compile-and-runtime invariant, not a convention
  • Permissions over roles
  • Refresh-token family revocation
  • Six-document architecture and security spec, including what is deliberately not claimed