Multi-tenant
Multi-tenant (v1.20.0+)
- A multi-tenant instance serves many customers (tenants) with one set of APIs. Every tenant has its own database, of the same type as the instance.
- The APIs, schemas, hooks, settings and permissions of the instance are defined once and serve every tenant. Customers share the servers and the APIs, not the databases.
- The instance keeps its own connection string. That database is the structure database: a request without a tenant works on it.
- Supported for MongoDB, MySQL, MariaDB, SQL Server, PostgreSQL and Oracle instances.
Example: a CRM with two customers
- A CRM is sold to two companies, Acme and Globex. Each one gets its own PostgreSQL database:
crm_acmeandcrm_globex. - One instance named
crmserves both. Its own connection string points tocrm, the structure database, which holds the tables every tenant has:customers,orders,users. - The tenants live in a table
tenantsof another instance,catalog, one row per customer.
1. Keep your tenants in a table
- Use a table of any instance, with one row per tenant: a username column and a connection string column.
- The table needs an active schema in API Maker.
- To keep connection strings encrypted, add
conversions.encryptionto that column in its schema. API Maker decrypts it with the default secret when it connects. - For Oracle tenants, add columns for the username, password and privilege of each tenant.
- Tenants can use different database servers: here Acme is on
db-1and Globex ondb-2.
2. Point the default secret to that table
3. Mark the instance
- Open the instance and check Is Multi Tenant Structure Instance.
- In Connection String Multi Tenant, choose the entry of the secret, for example
multiTenant.crm.
4. Name the tenant in every request
- In the path, after the instance name, with two colons.
- Or with two headers. Send both of them, or none.
- Custom APIs read the tenant of the request in
g.req.params.tenantUsername.
What API Maker does
- Reads the row of the tenant from the tenants table, decrypts its connection string if needed, and opens a connection pool for that tenant. The next requests of the tenant reuse the pool.
- A new tenant needs no restart and no deployment: its first request reads its row. Onboarding a customer is creating its database and adding one row to the tenants table.
- The tenant is part of every cache key, so tenants never get each other's cached answers.
- Logs record the tenant of each call.
- The databases panel and the API testing page let you pick a tenant to work with its data.
Users of a tenant
- Users can live in a users table inside each tenant database, like
crm.usersabove. - Add a DB token generator on that table of the multi-tenant instance, on the Token Generators page, for example
crm_users_token. - A user gets a token with the token API and the tenant headers:
|
- API Maker reads Alice from the users table of
crm_acme, and the token it returns is made foracme:- Requests of
acmeaccept it, as long as Alice is in the users table ofcrm_acme. - A request for
globexis refused with 401:Tenant from x-am-user-authorization and x-am-tenant-username should be same. - A request without a tenant, on the structure database, is refused with 401.
- A refreshed token keeps the tenant.
- Requests of
- A token made without a tenant, or from a users table in another instance, is not tied to a tenant: it is checked against the users table of the tenant each request names. To keep such a caller to its own tenant, compare
g.req.params.tenantUsernamewith the tenant of the caller in a pre hook.
After a tenant moves
- When the connection string of a tenant changes, call the Multi tenant instance updated system API. Every server drops the pool of that tenant and the cache of the instance is cleared.
- It also accepts an array of
{ instanceName, username }.
Good to know
- A schema change must run on every tenant database: plan migrations for all of them.
- Each API Maker process keeps one pool per active tenant. Size the connection limits of your database servers for it.