An example to illustrate how to enable multi-tenancy with LoopBack's IoC and DI container
npm install @loopback/example-multi-tenancyAn example application to demonstrate how to implement multi-tenancy with
LoopBack 4.
This interface defines the contract for multi-tenancy strategies to implement
the logic to identify a tenant and bind tenant specific resources to the request
context.
``ts
/**
* Interface for a multi-tenancy strategy to implement
*/
export interface MultiTenancyStrategy {
/**
* Name of the strategy
*/
name: string;
/**
* Identify the tenant for a given http request
* @param requestContext - Http request
*/
identifyTenant(
requestContext: RequestContext,
): ValueOrPromise
/**
* Bind tenant-specific resources for downstream artifacts with dependency
* injection
* @param requestContext - Request context
*/
bindResources(
requestContext: RequestContext,
tenant: Tenant,
): ValueOrPromise
}
`
MultiTenancyActionProvider serves two purposes:
- Provides an action (MultiTenancyAction) for the REST sequence to enforce
multi-tenancy
- Exposes an extension point to plug in multi-tenancy strategies
The example includes a few simple implementations of MultiTenancyStrategy:
#### Identify tenant id for a given http request
- JWTStrategy - use JWT token from Authorization headerx-tenant-id
- HeaderStrategy - use headertenant-id
- QueryStrategy - use query parameterhost
- HostStrategy - use header
#### Bind tenant specific resources to the request context
We simply rebind datasources.db to a tenant specific datasource to select theUserRepository
right datasource for .
`tsdatasources.db.${tenant.id}
bindResources(
requestContext: RequestContext,
tenant: Tenant,
): ValueOrPromise
requestContext
.bind('datasources.db')
.toAlias();`
}
Multi-tenancy strategies are registered to the extension point using
extensionFor template:
`ts`
app.add(
createBindingFromClass(JWTStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
);
We group multiple registrations in src/multi-tenancy/component.ts using theMultiTenancyComponent:
`ts`
export class MultiTenancyComponent implements Component {
bindings = [
// Add the action
createBindingFromClass(MultiTenancyActionProvider, {
key: MultiTenancyBindings.ACTION,
}),
// Add strategies
createBindingFromClass(JWTStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
createBindingFromClass(HeaderStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
createBindingFromClass(QueryStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
createBindingFromClass(HostStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
];
}
The MultiTenancyAction can be configured with what strategies are checked in
order.
`ts`
app
.configure
.to({strategyNames: ['jwt', 'header', 'query']});
MultiTenancyAction is added to src/sequence.ts so that REST requests will be
intercepted to enforce multiple tenancy before other actions.
`ts
export class MySequence implements SequenceHandler {
constructor(
// ...
@inject(MultiTenancyBindings.ACTION)
public multiTenancy: MultiTenancyAction,
) {}
async handle(context: RequestContext) {
try {
const {request, response} = context;
await this.multiTenancy(context);
// ...
} catch (err) {
this.reject(context, err);
}
}
}
`
`sh`
npm start
The strategies expect clients to set tenant id for REST API requests.
- jwt: set Authorization header asAuthorization: Bearer
header
- : set x-tenant-id header as x-tenant-id: query
- : set tenant-id query parameter, such as: ?tenant-id=
Check out acceptance tests to understand how to pass tenant id using different
strategies:
- src/tests/acceptance/user.controller.header.acceptance.ts
- src/tests/acceptance/user.controller.jwt.acceptance.ts
You can use environment variable DEBUG=loopback:multi-tenancy:* to print out
information about the multi-tenancy actions.
Run npm test` from the root folder.
See
all contributors.
MIT