Blog · Guide
Error tracking for NestJS and Node.js: a practical setup
NestJS catches every exception for you and turns it into a tidy JSON response. That's great for your clients and terrible for you: the error is handled, so nothing tells you it happened.
Why errors go missing in NestJS
Nest's built-in exception layer catches anything a controller throws. A TypeError deep in a service becomes a 500 response, gets logged to stdout, and that's it. Unless someone is reading the logs, a broken endpoint can sit there for days.
The fix is to hook into that same layer and send every unexpected exception to an error tracker - once, with the request that caused it.
1. Install and initialise
npm install bugcatch-sdk
Initialise as early as possible in main.ts, before the app is created. Breadcrumb auto-capture is for browsers, so turn it off on the server:
import BugCatch from 'bugcatch-sdk';
BugCatch.init({
dsn: process.env.BUGCATCH_DSN!,
release: process.env.npm_package_version,
environment: process.env.NODE_ENV,
autoCaptureBreadcrumbs: false,
});
Uncaught exceptions and unhandled promise rejections are now captured automatically. Errors inside request handlers need one more step.
2. A global exception filter
Extend Nest's BaseExceptionFilter so responses keep working exactly as before, and report the exception on the way through:
import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import BugCatch from 'bugcatch-sdk';
@Catch()
export class BugCatchFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const req = host.switchToHttp().getRequest();
BugCatch.captureException(exception, {
user: req.user ? { id: req.user.id, email: req.user.email } : undefined,
request: { method: req.method, url: req.url },
});
super.catch(exception, host);
}
}
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new BugCatchFilter(httpAdapter));
Duplicates are fingerprinted into one issue, so a bug that fires on every request shows up once with a count, not as thousands of entries.
3. Don't report 404s and 400s
This is where most NestJS setups get noisy. Every bot probing /wp-admin or /.env throws a NotFoundException. Every malformed form post throws a BadRequestException. Report all of them and your real bugs drown.
The BugCatch SDK skips any error carrying a 4xx status by default - it reads Nest's getStatus(), so NotFoundException, BadRequestException, UnauthorizedException and friends never open an issue. 5xx errors and plain Errors are always reported. Errors from your own outgoing requests (axios, got, ky) are also always reported, since a 404 from an API you call is usually your bug.
If you do want client errors for a particular app, switch them back on:
BugCatch.init({ dsn, captureClientErrors: true });
4. Trace slow routes and queries
Errors are half the picture; the other half is the endpoint that technically works but takes four seconds. Nest runs on Express by default, so the tracing middleware plugs straight in, and TypeORM and ioredis can be instrumented in one line each:
app.use(BugCatch.tracingMiddleware({ ignoreRoutes: ['/health'] }));
BugCatch.instrumentTypeOrm(dataSource); // an initialised DataSource
BugCatch.instrumentIoredis(Redis); // the class, not an instance
Each request becomes a transaction named by its route (GET /orders/:id), with database queries and cache commands as spans in a waterfall. Requests are grouped by route, so you see percentiles per endpoint rather than one average that hides the slow tail.
5. Server metrics
Heap, CPU and event-loop lag per instance tell you whether one pod is struggling or the whole fleet is. Wrap the reporter in a provider so Nest owns its lifecycle:
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { BugCatchServerReporter } from 'bugcatch-sdk';
@Injectable()
export class BugCatchMetricsService implements OnModuleInit, OnModuleDestroy {
private readonly reporter = new BugCatchServerReporter({
dsn: process.env.BUGCATCH_DSN!,
});
onModuleInit() { this.reporter.start(); }
onModuleDestroy() { this.reporter.stop(); }
}
You can then set alerts on memory, CPU and slow endpoints, each with its own cooldown.
Plain Express works the same way
Without Nest, register an error middleware last:
app.use((err, req, res, next) => {
BugCatch.captureException(err, { path: req.path, method: req.method });
next(err);
});
The full option list is in the Node.js docs.