fastify / fastify/help

How to initialize repositories with other plugins information?

Open
#850 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.