Papooch / nestjs-cls

A continuation-local storage (async context) module compatible with NestJS's dependency injection.
https://papooch.github.io/nestjs-cls/
MIT License
390 stars 23 forks source link

Integrating with RLS #73

Closed nugmanoff closed 12 months ago

nugmanoff commented 1 year ago

Hey @Papooch! Thanks for an amazing library! I've been trying to fit it properly in my Nest.js + TypeORM project, where I am trying to implement RLS-based multi-tenancy for PostgresDB.

I am using rls library to achieve that. One downside is that it uses Request.SCOPE. I wanted to work around it using nestjs-cls which seems like a perfectly doable thing.

This part from nestjs-cls use cases examples

ClsModule.forFeature({
    provide: TENANT_CONNECTION,
    import: [DatabaseConnectionModule],
    inject: [CLS_REQ, DatabaseConnectionService],
    useFactory: async (req: Request, dbService: DatabaseConnectionService) => {
        const tenantId = req.params['tenantId'];
        const connection = await dbService.getTenantConnection(tenantId);
        return connection;
    },
});

Looks like this part of the code from cls library.

So as I tried just replacing Request with CLS_RQS there in my own fork, but it didn't work, because it was trying to instantiate the rls module too early, at the application bootstrap time, when I don't have access to the request yet.

Now when when I am trying to do it with ClsModule.forFeature as in the example above – I am struggling with making TENANT_CONNECTION avialble in RLSModule as well.

Can you please nudge me in the right direction of how to do it properly? I am sure it could be done really easily, but couldn't just wrap my head around it.

Papooch commented 1 year ago

I have no experience with the libray you talk about, but please have a look at this feature, mabye it solves your issue: https://papooch.github.io/nestjs-cls/features-and-use-cases/proxy-providers#delayed-resolution-of-proxy-providers

nugmanoff commented 1 year ago

Hey @Papooch, Thanks for the quick response!

I am very much sure that using your library I can break out of Request.SCOPE dependency in that library – I just can't wrap my head around ClsModule.forFeature thing.

I know that you don't have to do that and it is not directly related to your library – but if you could take a look into this, I'd help me very much!

Shall I provide a greater context here or I can I reach out to you and borrow 15 minutes of your time? (and not pollute this repo)

Papooch commented 1 year ago

I would prefer if you give the details here, so other people doing the same may benefit from it, too.

I am not exactly sure which part do you not understand, so please do elaborate.

The ClsModule.forFearure basically instantiates a JavaScript Proxy of your provider as a singleton, but redirects all access to it's members to an unique instance stored in the CLS. That instance is created as soon as the CLS context is created, but can be delayed using the technique I shared above.

Papooch commented 1 year ago

@nugmanoff Do you require any further assistance with this, or can I close the issue? I don't know how to proceed from my side.

nugmanoff commented 1 year ago

Hey @Papooch! Sorry for going dark for 2 weeks – I was experimenting a lot on how to integrate CLS & RLS together and eventually ended up including RLS as source code inside my project and changing implementation in few places – and it worked, but it is not perfect because I have trouble with some of the CLS-depended registrations being undefined – I am sure it could be resolved with resolveProxyProviders, but I didn't yet have time to take a detailed look at it once again.

I'll get back to you as soon as take a look it once again. Thanks for your time! Appreciate it.

Papooch commented 12 months ago

I'm going to close this issue for now, feel free to tag me here with more info that I can investigate when you have some time and I'll reopen it so we can continue the discussion here.