Isolation by design
Each customer has its own database, so there is no tenant_id filter to add to every query, or to forget.
API Maker
The framework for AI era
Database APIs
One set of APIs for all your customers, each with a database of its own.
Mark an instance as a multi-tenant structure and point it to a tenants table. Each request names its tenant, and API Maker runs the same APIs, schemas, hooks and permissions on that tenant's database, whose connection string it reads from the tenants table. Customers share the servers and the APIs, not the databases.
Step through it, slow it down, or open the full canvas.
The request names its tenant. crm::acme in the path: the instance crm, the tenant acme. The same API serves every customer.
crm::acme in the path: the instance crm, the tenant acme. The same API serves every customer.
It finds acme in the tenants table, with the find of the secret entry, and decrypts its connection string.
A pool opens for crm::acme and the query runs on the database of Acme. Only the customers of Acme come back.
globex comes in a header. Its row gives another connection string, another pool and another database.
Acme asks again. Its pool is open, so the query goes straight to its database, without reading the tenants table.
Each customer has its own database, so there is no tenant_id filter to add to every query, or to forget.
Schemas, hooks, settings, permissions and custom code are defined once and apply to every tenant.
Your users can live in a users table inside each tenant database. A user token made for a tenant works for that tenant only.
The tenant is part of every cache key, so one customer never receives the cached answer of another.
Copy its database, update its row, and call the multi-tenant-instance-updated system API: every server drops the pool of that tenant and clears the cache of the instance.
Pick a tenant in the databases panel and on the API testing page to work with its data. Logs record the tenant of each call.
A CRM sold to companies keeps each company in its own PostgreSQL database. Small customers share a database server, a large one gets its own, and a regulated one keeps its database in its own data center. The product has one set of APIs, and onboarding a customer is creating its database and adding one row to the tenants table.
import * as T from 'types';let Secret: T.ISecretType | any = { // … common and your other keys multiTenant: { crm: <T.IMultiTenantSecretObj>{ instanceName: 'catalog', databaseName: 'public', collectionName: 'tenants', usernameColumn: 'username', connectionStringColumn: 'connection_string', find: { active: true }, }, },};module.exports = Secret;Then choose multiTenant.crm as "Connection String Multi Tenant" on the instance crm. For Oracle, add the columns of the username and password of each tenant, and of its privilege when it connects with one.
GET /api/gen/admin/crm::acme/crm/customersx-am-authorization: <API user token>GET /api/gen/admin/crm/crm/customersx-am-authorization: <API user token>x-am-tenant-username: globexThe first request runs on the database of Acme, the second on the database of Globex. The instance finds the tenant in its own tenants table, so the request names the tenant only.
await g.sys.system.multiTenantInstanceUpdated({ instanceName: 'crm', username: 'globex' });Keep your tenants in a table that has a schema in API Maker, with a username column and a connection string column. Add an entry for that table to the default secret. Then check "Is Multi Tenant Structure Instance" on the instance and choose that entry in "Connection String Multi Tenant".
Its own connection string, the structure database. It holds the tables whose schemas every tenant uses. A request without a tenant works on it.
Call the token API with the tenant header and the name of a DB token generator on the users table of the multi-tenant instance. The user is read from the database of that tenant, and the token works for that tenant only, also after a refresh.
From the account of the signed-in user. Put it in the path (crm::acme) or in the x-am-tenant-username header of every request.
Yes. Each one has its own secret entry and tenants table, and its own database type.
The first request of a new tenant reads its row and opens its pool. No restart and no deployment.
Documentation