So far Sentry doesn’t have integration directly with NestJS, so let’s see how to make the integration with NestJS Exception Filters. Worth mentioning that we will use some specific configurations for Express, the default HTTP server Nest.js uses under the hood. If you use Fastify you will need some tweaks in the configuration.

1. Create a new Node.js based project in Sentry

Select Node.js and complete the form. If they ask if you use a framework on the project creation skip it.

Screenshot 2023-10-07 at 9.31.06 PM.png

2. Install Sentry and add the configuration

After creating the project, Sentry will show you the configuration details for your project.

Screenshot 2023-10-07 at 9.42.52 PM.png

For the minimum basic configuration, we don’t need Performance Monitoring or Profiling, so let’s install only @sentry/node

npm install @sentry/node

Now let’s create the src/sentry.filter.ts. We are generating a generic ExceptionFilter that captures any type of Exception because the @Catch() decorator is empty. Also, we are extending it from BaseExceptionFilter, which is Nest's default class for handling exceptions. Therefore, we will not change the original behavior when an exception is thrown because the method of the parent class that handles exceptions is called at the end of the code: super.handleUnknownError(exception, host);. Also notice we are implementing only the handleUnknownError method, this way we won’t pass to Sentry exceptions that are handled correctly in our system (BadRequestNotAuthorized, etc.).

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

@Catch()
export class SentryFilter extends BaseExceptionFilter {
  handleUnknownError(exception: unknown, host: ArgumentsHost, applicationRef: HttpServer): void {
    // If you want to avoid sending errors when running in local environment
    // you can wrap the captureException in 
    // if (host.switchToHttp().getRequest().hostname !== 'localhost')
    
    Sentry.captureException(exception);
    super.handleUnknownError(exception, host, applicationRef);
  }
}

Finally, let’s include in your app boostrap() (normally is in src/main.ts) the Sentry initialization, and an extra configuration to add the request object in the captured exception as is mentioned in the Sentry Express guide

import { NestFactory, HttpAdapterHost } from '@nestjs/core';
import * as Sentry from '@sentry/node';
import { SentryFilter } from './sentry.filter';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Sentry initialization
  Sentry.init({ 
    environment: process.env.ENVIRONMENT,  // Capture your environment
    dsn: '<https://[email protected]/xxx>' // Put your DSN link
  });

  // The request handler must be the first middleware on the app
  app.use(Sentry.Handlers.requestHandler());
  
  // ... your other middlewares

  // Import the filter globally, capturing all exceptions on all routes
	const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new SentryFilter(httpAdapter));

  // The error handler must be before any other error middleware and after all controllers    
  app.use(Sentry.Handlers.errorHandler());

  await app.listen(process.env.PORT || 8080);
}

Configuration completed 🎉🎉.

Check issues in Sentry

Now you just need to generate an exception in any part of your application and take a look in Sentry issues page. Your issue should look similar to this

Screenshot 2023-10-07 at 10.32.58 PM.png

Notice that Sentry is capturing correctly all the information from the request object (user email, user ID, url, etc.).