0

I'm trying to create some custom exceptions for my application. This mostly means i'm extending the HttpException class which is pretty simple.

However, as part of the exception message, I want to pass some of the configurations for the application.

The problem is that Exceptions are not part of the module or service. They're not managed classes, so I cannot use the ConfigService as described by the NestJS documentation.

I could use the process.env.<my_config> approach, but it seems dirty to have to use that when I'm using ConfigService everywhere in my services, specially when i'm also using .env files to load some other variables. My last alternative would be to use dotenv directly in the configuration. However all of them suffer from the same: I could be missing some important data updates/added during the app bootstrapping portion.

How can I access app level configurations from outside the managed classes?

Sample of what i'm trying to do:

import { HttpException, HttpStatus } from '@nestjs/common';

export class MyCustomException extends HttpException {
  
  constructor(message) {
    const serviceName = // Get the configuration value
    const configA = // Get other configuration value

    const payload = {
      serviceName,
      configA,
      message,
    }
    super(payload, HttpStatus.BAD_REQUEST)
  }
}
roloenusa
  • 761
  • 1
  • 10
  • 19
  • if you'll initialize `MyCustomException` by yourself like `new MyCustomException()`, you won't be able to use `ConfigService`. – Micael Levi Jul 19 '22 at 18:26
  • Right, i understand that. I'm looking for a way to solve that specific issue. Being able to use something so integral as configurations, outside of a service, seems pretty important. Otherwise your code starts getting fragmented between what you load via dotenv vs configservice. – roloenusa Jul 19 '22 at 20:01

1 Answers1

0

You can create an exception filter to catch your custom exception or the built-in BadRequestException, then inject ConfigService into the filter when registering the filter in main.ts.

  1. create custom exception filter to catch your custom exception.

my-custom-exception.filter.ts

import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus} from '@nestjs/common';
import { Response } from 'express';
import { ConfigService } from '@nestjs/config';
import { MyCustomException } from './my-custom.exception'; // replace this to your exception file path

@Catch(MyCustomException)
export class MyCustomExceptionFilter implements ExceptionFilter {
  constructor(private readonly _configService: ConfigService) {}

  catch(exception: MyCustomException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    // get configuration from .env
    const serviceName = this._configService.get<string>(`SERVICE_NAME`); // replace to your key in .env
    const configA = this._configService.get<string>(`CONFIG_A`); // replace to your key in .env

    response
      .status(HttpStatus.BAD_REQUEST)
      .json({
        serviceName,
        configA,
        message: exception.message
      });
  }
}
  1. Pass configService to your custom exception filter when initializing app.

main.ts

...
const app = await NestFactory.create(AppModule);

// get configService instance, pass into filter
const configService = app.get<ConfigService>(ConfigService);
app.useGlobalFilters(new MyCustomExceptionFilter (configService));
...
username_jj
  • 245
  • 1
  • 10
  • I'm currently using an exception filter already for some other reasons (creating a centralized log for exceptions). However, i don't think this is a healthy alternative for the specific problem set. If we have 20 different exception with each grabbing 20 different environment variables, i then need to create 20 different filters and add them to the app. Even if i use a global filter, I then have to do 20 if statements when this could have been just independently encapsulated. It seems shortsighted to not be able to access this super important data from anywhere in the app. – roloenusa Jul 20 '22 at 14:07
  • you can have all 20 custom exceptions to extends from a more generic parent class. All children exceptions that extends from the parent class inherit a `keys` array property which represents config that they want to resolve. Use parent class in filter `@catch()` decorator, then loop through the keys provided by the exception to dynamically load configurations that you need. – username_jj Jul 21 '22 at 05:15
  • another workaround is create a `ExceptionFactory` class which configService can be injectable, inject `ExceptionFactory` to anywhere you want to throw custom exception, then instead of `throw new MyCustomException()` you do `this._exceptionFactory.throwMyCustomException()` which internally resolve configurations and throw the exception. Alternatively, I can think of is using [interceptors](https://docs.nestjs.com/interceptors#exception-mapping) to catch your custom exception then resolve configs from configService and transform to `BadRequestException`, similar to filter approach – username_jj Jul 21 '22 at 05:30
  • If you think the solutions are not good enough, maybe the others in official nestjs [discord](https://discord.gg/qwYRznpT) can help. – username_jj Jul 21 '22 at 05:34