Who doesn’t love some nice graphs, metrics and profiles? Let’s have a look at how to setup Sentry in NestJS.

The module file

import * as Sentry from '@sentry/node';
import { INestApplication, Module } from '@nestjs/common';
import { nodeProfilingIntegration } from '@sentry/profiling-node';
import { APP_FILTER } from '@nestjs/core';
import { SentryFilter } from './sentry.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: SentryFilter,
    },
  ],
})
export class MonitoringModule {
  public static setup(app: INestApplication) {
    if (!process.env.SENTRY_DSN) {
      return;
    }

    Sentry.init({
      dsn: process.env.SENTRY_DSN,
      integrations: [
        nodeProfilingIntegration(),
        new Sentry.Integrations.Http({ tracing: true }),
        new Sentry.Integrations.Express({ app: app.getHttpServer() }),
      ],
    });

    app.use(Sentry.Handlers.requestHandler());
    app.use(Sentry.Handlers.tracingHandler());
  }
}

Sentry should be initialized as soon as possible, this is why I’ve chosen to create a static method, so it can be called from main.

NestJS still is an Express server, we will use Sentry’s “Express” and “Http” integrations. And register the express middleware onto the NestApplication.

Sentry’s errorHandler cannot be used, since it must be registered before other error handlers and after all other middleware. This is why we create an exception filter.

Exception Filter

import { ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import * as Sentry from '@sentry/node';

export class SentryFilter extends BaseExceptionFilter {
  catch(exception: any, host: ArgumentsHost): void {
    if (!(exception instanceof HttpException)) {
      Sentry.captureException(exception);
    }

    super.catch(exception, host);
  }
}

The filter simply captures an error to be sent to Sentry, and rethrows it.

Tying it together

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MonitoringModule } from './monitoring/monitoring.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  MonitoringModule.setup(app);
  await app.listen(process.env.PORT || 3000);
}
bootstrap();

import { Module } from '@nestjs/common';
import { MonitoringModule } from './monitoring/monitoring.module';

@Module({
  imports: [
    MonitoringModule,
  ],
})
export class AppModule {}

Be sure to register MonitoringModule first, so that the exception filter is the first filter in the chain.