Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use in a standalone CLI app? #97

Closed
sam-artuso opened this issue Jan 15, 2024 · 3 comments
Closed

How to use in a standalone CLI app? #97

sam-artuso opened this issue Jan 15, 2024 · 3 comments
Labels
question Further information is requested solved The question was answered or the problem was solved

Comments

@sam-artuso
Copy link
Contributor

I'm using nestjs-cls to create a database transaction object that is created for each request and propagate it through the app.

My app can also work in CLI only mode – there are no network listeners and no REQUEST. I followed this article.

What is the best way to create a transaction object in such a way that it works both in normal mode and in CLI mode?

@Papooch
Copy link
Owner

Papooch commented Jan 16, 2024

I am working on a new plugin for transactions that should make it seamless: #96

In the meantime, all you really need to do (if you already have the mechanism for creating and storing the transaction object in the CLS) is to make sure that the CLS context is initialized. For HTTP based apps (where request exists), this is as easy as registering the root module with the mount option for an enhancer. When there is no request, you need to wrap the entry point yourself: https://papooch.github.io/nestjs-cls/features-and-use-cases/usage-outside-of-web-request

From an architecture "best practises" perspective:

  • Say that there is a CatsService that relies on CLS context to be initialized and is to be used in both the http-based app and a standalone CLI app.
  • For the HTTP based app, there is a CatsController that automatically uses the ClsMiddleware (or other enhancer configured by the root module).
  • For the CLI app, there should be a different entry point, say a CatsCommandController, whose methods can be decorated by the @UseCls decorator.
  • That way, the service itself doesn't really care where its called from as long as the caller makes sure to initialize the context.

@Papooch Papooch added the question Further information is requested label Jan 16, 2024
@Papooch
Copy link
Owner

Papooch commented Jan 17, 2024

A CLI App usually starts and bootstraps the entire application on each command invocation, therefore you can probably wrap the call just after the application bootsraps, like so:

async function bootstrap() {
  const app = await NestFactory.createApplicationContext(AppModule)
  // [...] some more setup

  const commandController = app.get(CommandController)

  await app.get(ClsService).run(
    () => commandController.runCommand()
  )

  await app.close()
}

@sam-artuso
Copy link
Contributor Author

I ended up doing something very similar:

import { CommandFactory } from 'nest-commander'
import { ClsService } from 'nestjs-cls'

import { AppModule } from './app.module'
import { getAuthTokenForCliMode } from './common/cli'

import type { DbTx } from './db/types'

async function bootstrap() {
  const app = await CommandFactory.createWithoutRunning(AppModule)

  const clsService = app.get(ClsService)
  const dbService = app.get('Db')

  await clsService.run(() =>
    dbService.tx(async (tx: DbTx) => {
      clsService.set('Tx', tx)
      clsService.set('AuthHeader', `Bearer ${getAuthTokenForCliMode()}`)

      await CommandFactory.runApplication(app)
      await dbService.disconnect()
    }),
  )
}

// eslint-disable-next-line no-void
void bootstrap()

Thank you for your help!

@Papooch Papooch added the solved The question was answered or the problem was solved label Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested solved The question was answered or the problem was solved
Projects
None yet
Development

No branches or pull requests

2 participants