A multi-tenant SaaS boilerplate that isolates data by default
In a B2B product, one customer seeing another customer's data is the bug that ends the contract. Keelmint makes the safe path the default: data is scoped to an organization unless you deliberately say otherwise.
One deployment, many organizations
Each customer is an organization with its own members, roles and data. A person has one login and any number of memberships, switches between them from the header, and acts in one active organization at a time. Organizations are created by you as platform admin, or, if you turn it on, by any signed-in user who then becomes its admin.
Building an internal tool or a product for a single company? Set organizations: 'single' and the same
code runs with one built-in organization and no switcher.
Scoping that fails closed
Shared-schema multi-tenancy (an organizationId on each row) is the simplest model to run, and its one
weakness is the forgotten where. Keelmint removes that weakness: you mark a model, and a Prisma
extension adds the organization to every query on it.
model Project {
// @base
// @tenant
name String
}
- A query without an active organization is refused (
403 ORGANIZATION_REQUIRED), never answered with every tenant's rows. - Creates get the organization automatically; updates and deletes only reach the caller's own organization.
- A platform admin can work across organizations, or target one with a header, without special code in your routes.
- Background jobs and seeds say explicitly which organization they act for.
A route-keyed isolation test suite fails the build when an organization-scoped route has no isolation test, so new routes cannot quietly skip the check.
Units: branches, departments and their admins
Real companies are not flat. Each organization has a tree of units (for example a London branch with a Payroll department), and a role can be assigned to the whole organization or to one unit and everything below it.
- A unit admin manages only their subtree: its members, invitations, audit events and helpdesk tickets.
- Records owned by a person (
// @owned) are visible to that person, and to admins whose reach includes them. - Anyone can narrow a list to a unit or a member with filter headers, never past what they may see.
Roles that stay safe as customers edit them
Organization admins can create their own roles. The rules hold whatever they do: an organization admin always has what a unit admin has, a unit admin what a user has, and nobody grants a permission they lack. Removing the last admin of an organization is refused.
Billing per tenant
Subscriptions belong to organizations, with plan limits on members, units and your own counters, and a no-card trial. See the NestJS page for how limits are enforced.
See how it works in detail
The organizations and units and tenant data pages of the public docs describe every rule, including the measured limits of the reach queries.