How to initialize repositories with other plugins information?
- Dominant language
- No language data
- Stars
- 68
- Forks
- 8
- Avg merge
- 11h 2m
- Merged PRs (30d)
- 2
Description
#### What are you trying to achieve, or the steps to reproduce?
In a multi-tenant application, it's common to find that a database uses such identifier to separate data, and it often comes from a valid token in its payload, what I'm trying to achieve is to create my repository initialization in a way I won't need to deal with passing the tenant identifier all the time, for example:
```js
// database repository
export default class CustomerRepository {
private collection;
private log;
constructor(fastify: FastifyInstance) {
this.collection = fastify.mongo.db?.collection('customers');
this.log = fastify.log;
}
getCustomerByDocumentId = (tenantId: string, customerId: string) => {
this.log.info({ tenantId, customerId }, 'getCustomerByDocumentId');
return this.collection?.findOne({ tenantId, _id: new ObjectId(customerId) });
};
}
```
my authentication plugin extracts `tenantId` from the headers and add it to the request as a decorator
```js
// authentication plugin
type AuthUserContext = {
// ...
tenantId: string;
}
export default fp(async (fastify: FastifyInstance) => {
async function authentication(request: FastifyRequest<{ Headers: AuthHeaders }>, reply: FastifyReply) {
try {
const { user } = decodeInvoicesAuthContext(request.headers);
fastify.assert(user.tenantId, 'tenantId is required');
request.user = user;
} catch (error) {
fastify.log.error({ message: 'could not extract data', error });
void reply.unauthorized();
return false;
}
return true;
}
fastify.decorate('authentication', authentication);
});
declare module 'fastify' {
export interface FastifyRequest {
user: AuthUserContext;
}
export interface FastifyInstance {
authentication?: onRequestHookHandler;
}
}
```
and I've tried to use the plugin system to initialize the repository, and **works really well**
```js
// repository initialization as a plugin
export default fp(async (fastify) => {
await fastify.register(() => fastify.decorate('customersDb', new CustomerRepository(fastify)));
});
```
project structure example
```bash
-- src
-- plugins
authentication.js
repositories.ts
-- repositories
customers.ts
-- types
app.ts
```
#### What have you tried?
I was wondering if there is a way to pass that authentication decorator somehow into the repository initializer, so I don't need to worry all the time about passing the `tenantId` for each request
if I call `fastify.hasDecorator('authentication')` from the repository plugin, I get a `truthfully`
```js
// repository initialization as a plugin
export default fp(async (fastify) => {
if(fastify.hasDecorator('authentication')) {
// this is always "true"
}
await fastify.register(() => fastify.decorate('customersDb', new CustomerRepository(fastify)));
});
```
#### What are you expecting?
I've been thinking if there's a nice way to have something as
```js
getCustomerByDocumentId = (customerId: string) => {
const { tenantId } = this.fastify.request.user; // get user object from request "automagically"
return this.collection?.findOne({ tenantId, _id: new ObjectId(customerId) });
};
```
How do you initialize your database repositories if you need some information from other plugins, like authentication?
#### Context
* *node version*: 18.x
* *fastify version*: 4.14.x
* *os*: Mac
Contributor guide
Assessment
This issue has not been assessed yet.