Papooch / nestjs-cls

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

Add ClsInterceptor to avoid having to use enterWith with GraphQL #5

Closed andreialecu closed 2 years ago

andreialecu commented 2 years ago

Hey there, just came across this library while investigating my own issues with AsyncLocalStorage losing context inside graphql interceptors.

I'm not (yet) using this library, I have a custom implementation, but I wanted to mention that I eventually got it working via this pattern in the interceptor:

  intercept(...) {
    return new Observable((observer) => {
      storage.run(myContext, () => {
        next
          .handle()
          .pipe(...)
          .subscribe({
            next: (res) => observer.next(res),
            error: (error) => observer.error(error),
            complete: () => observer.complete(),
          });
      });
    });
  }

Seems that wrapping the return in a cold observable is necessary, but this is all that I needed to do.

With this I can use run instead of enterWith, which I noticed was mentioned as a caveat in the readme about not being supported.

Papooch commented 2 years ago

Huh, that's very interesting, I'm going to look into it. I might add this as an alternative solution if you don't mind. Though setting up context in an interceptor would still make it unavailable in guards (which is where I'm mainly using it in my project) and I suspect this trick wouldn't be possible in a guard..

andreialecu commented 2 years ago

I might add this as an alternative solution if you don't mind

Of course. Just make sure you verify it first. I'm not sure if the reason it works is specific to my project or not.

Indeed, from what I saw, this method doesn't pass the context into guards. I don't currently need the AsyncLocalStorage context in guards though, I can use the regular Apollo/GraphQL context there instead.

Papooch commented 2 years ago

I just implemented your solution and it works great, thanks a lot again! When testing, I found it strange, that setting up the context in an interceptor causes it to be lost in an Exception Filter but only for REST (which is a non issue as you can use middleware there), but it works fine with GQL.