examples/multi-tenancy/README.md
An 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.
/**
* 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<Tenant | undefined>;
/**
* Bind tenant-specific resources for downstream artifacts with dependency
* injection
* @param requestContext - Request context
*/
bindResources(
requestContext: RequestContext,
tenant: Tenant,
): ValueOrPromise<void>;
}
MultiTenancyActionProvider serves two purposes:
MultiTenancyAction) for the REST sequence to enforce
multi-tenancyThe example includes a few simple implementations of MultiTenancyStrategy:
Authorization headerx-tenant-id headertenant-id query parameterhost headerWe simply rebind datasources.db to a tenant specific datasource to select the
right datasource for UserRepository.
bindResources(
requestContext: RequestContext,
tenant: Tenant,
): ValueOrPromise<void> {
requestContext
.bind('datasources.db')
.toAlias(`datasources.db.${tenant.id}`);
}
Multi-tenancy strategies are registered to the extension point using
extensionFor template:
app.add(
createBindingFromClass(JWTStrategy).apply(
extensionFor(MULTI_TENANCY_STRATEGIES),
),
);
We group multiple registrations in src/multi-tenancy/component.ts using the
MultiTenancyComponent:
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.
app
.configure<MultiTenancyActionOptions>(MultiTenancyBindings.ACTION)
.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.
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);
}
}
}
npm start
The strategies expect clients to set tenant id for REST API requests.
jwt: set Authorization header as
Authorization: Bearer <signed-jwt-token>header: set x-tenant-id header as x-tenant-id: <tenant-id>query: set tenant-id query parameter, such as: ?tenant-id=<tenant-id>Check out acceptance tests to understand how to pass tenant id using different strategies:
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