nestjs-cls icon indicating copy to clipboard operation
nestjs-cls copied to clipboard

[Feature] Disposal hook for request-scoped providers

Open eddienubes opened this issue 9 months ago • 2 comments

Hi! Currently, there's a way to define a factory for an async provider, e.g.

    ClsModule.forFeatureAsync({
      provide: MCP_CLIENT_TOKEN,
      strict: true,
      inject: [McpClientConfig, CLS_REQ],
      useFactory: async (config: McpClientConfig, req: Request) => {
        const client = new McpClient(config);
        await client.connect();
        return client;
      },
    }),

However, upon request completion I'd like to disconnect to release the session. This is a perfect place for disposal hook. Reading through nestjs issues, I stumbled across this one https://github.com/nestjs/nest/issues/9497, seems like the official advice is to use req.res.on('finish'). However, since nestjs-cls operates outside regular request scope, I was wondering if there's a nestjs-cls-native way of doing this, and if there's not, it'd be a nice feature to have.

Use case:

MCP client to reuse across request lifecycles and to disconnect at the end.

Thank you!

eddienubes avatar Aug 12 '25 16:08 eddienubes

Thank you for the feature request, it definitely sounds useful!

However, it sounds pretty tricky, let me think of possible ways to implement it.

The biggest obstacle that I see is that the lifetime of AsyncLocalStorage does not end when the run callback finishes.

One would think that we could just call the dispose hook once the request finishes, but all asynchronous calls triggered from within the run callback, even promises that you never await, still have access to the same context - this could lead to acessing a released resource, leading to crashes.

Perhaps there's some way to track if context is still active using other async_hooks API, but this definitely not a trivial task.

Papooch avatar Aug 12 '25 17:08 Papooch

@Papooch Theoretically, we could raise an exception only for providers with disposal hook defined preserving backwards compatibility, if anyone uses ALS in a weird way (unawaited promises etc.)

eddienubes avatar Aug 13 '25 12:08 eddienubes