Isolation by design
Each customer has its own database. No tenant_id to forget in a query, no way to read another tenant's data.
The framework for AI era
Level 3 · ScaleArchitecture 08 of 13
One product and one set of APIs, with the data of every customer in a database of its own.
True multi-tenancy, supported out of the box: each request names its tenant, and API Maker runs it on that customer's own database, found in a tenants table. Customers share the servers, never the data.
Every request is drawn as it travels. Slow it down, pause, go step by step, or open the full canvas and zoom in.
One set of APIs for every customer. Every customer calls the same APIs. The tenant is part of the path: /api/gen/saas/crm::acme/crm/customers.
The same steps as the diagram, in more detail.
The product has one set of APIs, schemas and custom code. The instance "crm" is marked as a multi-tenant structure, and each request names its tenant after two colons: crm::acme, crm::globex. API Maker runs the same API on the database of that customer, with the same permissions and hooks for all of them.
The instance points to a table of your own, through a secret: which column holds the username of the tenant, which one holds its connection string, and an optional filter such as active tenants only. On the first request of a tenant, API Maker reads its row, decrypts the connection string, opens a pool for that tenant and reuses it for the next requests.
Isolation does not rely on a tenant_id column that every query must remember: each customer has a separate database, so a request of one tenant is physically unable to read the data of another. Backups, restores, exports and deletions happen per customer.
Where a tenant's database lives is only a connection string. Put many small customers on a shared server, give a large one a server of its own, move one when it grows: copy its database, update its row, and call the multiTenantInstanceUpdated system API so every server resets its pool.
Onboarding a customer is creating its database, running your migrations on it, and inserting one row in the tenants table. A regulated customer can even keep its database in its own data center, reached through a private tunnel, while using the same product as everyone else.
API servers are shared by all tenants and scale like any fleet of API Maker: add servers behind the load balancer and each one serves every tenant, opening pools on demand. Cached responses are kept per tenant, so one customer never receives the cache of another.
Each customer has its own database. No tenant_id to forget in a query, no way to read another tenant's data.
The same APIs, schemas, permissions and custom code serve every customer. Fix once, every tenant gets it.
Small tenants share a server, large ones get their own, regulated ones keep their database at home.
Create the database, insert one row in the tenants table, and the new customer is served at once.
Connection strings in the tenants table can be encrypted by API Maker and are decrypted only when a pool opens.
Back up, restore, export, move or delete one customer without touching the others.
Plain VPS from any provider, or your own servers, with Ubuntu 22.04 LTS. Sizes are a starting point: measure and adjust.
Runs
Ports
Each server keeps a pool for every active tenant: plan the connection limits of the database servers for it.
Runs
Ports
Runs
Ports
Runs
{ "multiTenant": { "crm": { "instanceName": "catalog", "databaseName": "public", "collectionName": "tenants", "usernameColumn": "username", "connectionStringColumn": "connection_string", "find": { "active": true } } }}Then check "Is Multi Tenant Structure Instance" on the instance "crm" and set its "Connection String Multi Tenant" to multiTenant.crm.
GET /api/gen/saas/crm::acme/crm/customers # the database of AcmeGET /api/gen/saas/crm::globex/crm/customers # the database of Globex# A tenant moved to another server: its row is updated, then every server resets its poolPOST /api/system-api/saas/multi-tenant-instance-updated { "instanceName": "crm", "username": "globex" }Custom code calls it as sys.system.multiTenantInstanceUpdated.
The tenant is written after the instance name, separated by two colons: /api/gen/saas/crm::acme/…. Your frontend builds this path from the account the user belongs to, or a pre hook can set it from the token.
In a table of your own, the tenants table, with a column for the username of the tenant and one for its connection string. Turn on the encryption conversion of that column and API Maker stores it encrypted.
Not within one multi-tenant instance: its tenants share its engine, for example PostgreSQL. A product can have several multi-tenant instances, each with its own engine and tenants table.