NestJS: any way to create a provider for either a web-aware service, or a non web-aware service depending on context

Viewed 47

Background

I'm working on a large application that needs to be upgraded. If I were starting from scratch I'd do this all differently. But right now I need to figure out a fix without touching hundreds of files.

For the same reason, I ideally need this code to work on Nest 6. This project needs to be upgraded to the latest nest, but there are some things that need to be fixed to do this. Before I can do that, I need to resolve the current issue, which is blocking us from upgrading off of node 12

Problem

I have a logger class. This class is supposed to pull in some information from the REQUEST context, if one is available (basically, some headers). If no request context is available, this can be ignored.

For simplicity in talking about this, we can say that I need a provider Logger which returns either a RequestAwareLogger or PlainLogger instance, depending on whether or not it is being resolved from a request scope. Alternately, I need the provider to return the same class, with either a request injected (via @Inject(REQUEST)), or left undefined.

Edit For posterity: If I were writing this from scratch, I'd just update the logger.log call to consume this information directly by passing in the request object, or the fields I needed tracked. But since this is a huge project already, I'd have to modify 1000 lines of code in different files, many of which don't have direct access to the request. This will be a longer term effort

1 Answers

Unfortunately, there is no built-in way to do this in Nest. However, it is possible to create a custom provider that would achieve the same effect.

Here is an example provider that would return either a RequestAwareLogger or PlainLogger instance, depending on whether or not it is being resolved from a request scope:

@Injectable()
export class LoggerProvider {
  constructor(
    @Optional() @Inject(REQUEST) private readonly request?: Request,
  ) {}

  getLogger(): PlainLogger | RequestAwareLogger {
    // If a request is available, return a RequestAwareLogger instance
    if (this.request) {
      return new RequestAwareLogger(this.request);
    }
    // Otherwise, return a PlainLogger instance
    return new PlainLogger();
  }
}

Then, you can use this provider in your logger service like so:

@Injectable()
export class LoggerService {
  constructor(private readonly loggerProvider: LoggerProvider) {}

  log(message: string) {
    const logger = this.loggerProvider.getLogger();
    // Use the logger instance
    logger.log(message);
  }
}

Note that this provider will only work if Nest's IoC container is used to resolve the logger service. If you are using a different IoC container (e.g. in a non-Nest application), you will need to create a custom provider for that container.

Related