Basic proof of concept for multitenancy in LoopBack.
- Customer1
- datasource:
db
- instances:
[{name: 'Andy', name: 'Bob', name: 'Carol'}]
- Customer2
- datasource:
db2
- instances:
[{name: 'Dan', name: 'Eric', name: 'Francis'}]
START :
| : +---------------+ +--------------+
+--/api/customers?tenant=1----->|tenant resolver|--tenantId-->|model resolver|
: +---------------+ +--------------+
: |
: v
+--[{relevant models}]<--------------------------- (Customer + tenantId).find()
| :
END :
A request to /api/customers?tenant=1
comes in.
The tenant-resolver middleware parses the tenant identifier passed in the query
string. The tenant id is then stored in req.tenantId
and passed down to the
next middleware.
Next, the model-resolver middleware is triggered (and given the request with the
tenant identifier set -- req.tenantId
). The model resolver uses the tenant
id to determine which model to call.
For example, If tenant id is 1, use the Customer1 model (which is attached to
datasource 1 -- db
) and fetch it's associated data. If tenant id is 2,
use the Customer2 model (which is attached to datasource 2 -- db2
) and fetch
it's associated data.
We basically use a combination of middleware to get the tenant id and use it to
determine which physical model to call during runtime. In this case, we've
already preconfigured the physical models (using the logical models defined in
model.json
files) before the application is started.
- The way LB is coded today do not allow for multiple model definitions (we are
forced to create two separate models to prevent naming collisions --
Customer1
andCustomer2
instead of just one definitionCustomer
- In this POC, we hardcode model resolution to simplify the implemention (ie.
Customer + tenantId
, but in the real version, we should probably also dynamically determine the model to call (ie. Model.name + tenantId). This can easily be done by parsing the endpoint for the model name (ie. /api/customers]?tenant=1 -- the customers part of the URI for example) - Datasources cannot be changed dynamically during runtime since they are mapped in the configuration file before the app is started. We could probably do this with another resolution layer before actually fetching the instances
Run npm test
in the project root.