Setup Error Tracking (Sentry SDK) di NestJS
Dokumentasi ini buat masang error tracking (capture exception, request context, stack trace) ke instance Sentry-compatible (GlitchTip self-hosted atau Sentry Cloud) di aplikasi NestJS.
1. Prasyarat
- Sudah punya DSN dari project di dashboard GlitchTip/Sentry
(format:
https://<key>@<domain>/<project_id>) - Project NestJS pakai
.envbuat konfigurasi
2. Instalasi
npm install @sentry/nestjs --save
3. Environment variable
Tambahin di .env:
SENTRY_DSN="https://<key>@<domain>/<project_id>"
4. Buat file src/instrument.ts
File ini harus terpisah dari main.ts, dan berisi Sentry.init().
import 'dotenv/config'; // wajib, load .env sebelum Sentry.init() dipanggil
import * as Sentry from '@sentry/nestjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
sendDefaultPii: true, // biar IP, headers, user info ikut kecapture
environment: process.env.NODE_ENV || 'production',
// Konfigurasi eksplisit biar request body & query params PASTI kecapture
// (default behaviour beda-beda antar versi SDK, jadi jangan andelin default)
integrations: (defaultIntegrations) => [
...defaultIntegrations.filter((i) => i.name !== 'RequestData'),
Sentry.requestDataIntegration({
include: {
ip: true,
data: true, // request body
query_string: true, // query params
headers: true,
cookies: true,
user: true,
},
}),
],
});
Kenapa perlu
dotenv/configmanual:instrument.tsdiimport paling awal, sebelum NestJSConfigModulesempat jalan dan load.env. Tanpa ini,process.env.SENTRY_DSNmasihundefinedpasSentry.init()dipanggil, dan SDK diam-diam gak akan pernah ngirim event (cuma warning "No DSN provided" di log kalaudebug: true).
5. Import di baris paling atas main.ts
import './instrument'; // HARUS baris pertama, sebelum import apapun lain
import { NestFactory } from '@nestjs/core';
// ...import lain seperti biasa
Penting:
- Tanpa ekstensi
.tsdi akhir ('./instrument', bukan'./instrument.ts') - File-nya harus ada di
src/(folder yang sama denganmain.ts)
6. Register SentryModule & exception filter global di app.module.ts
import { APP_FILTER } from '@nestjs/core';
import { SentryModule, SentryGlobalFilter } from '@sentry/nestjs/setup';
@Module({
imports: [
SentryModule.forRoot(), // HARUS import PERTAMA di array imports
// ...module lain
],
providers: [
{ provide: APP_FILTER, useClass: SentryGlobalFilter }, // HARUS provider PERTAMA
// ...provider lain
],
})
export class AppModule {}
7. Kalau project sudah punya custom exception filter
Ini kasus yang sering kelewat. SentryGlobalFilter cuma nangkep exception yang
gak ketangkep filter lain duluan. Kalau ada filter spesifik seperti:
@Catch(PrismaClientKnownRequestError)
export class SomeCustomFilter implements ExceptionFilter { ... }
...exception yang match @Catch(...) itu tidak akan pernah sampai ke
SentryGlobalFilter, walaupun SentryModule sudah ke-setup benar. Solusinya:
tambahin Sentry.captureException(exception) manual di filter itu.
Aturan yang dipakai: status 5xx / error tak terduga → capture. Status 4xx yang memang disengaja (validasi, not found, unauthorized) → boleh skip, karena itu flow normal, bukan bug.
Helper biar konsisten di semua filter:
// src/common/sentry-report.util.ts
import * as Sentry from '@sentry/nestjs';
export function reportIfUnexpected(exception: unknown, httpStatus: number) {
if (httpStatus >= 500) {
Sentry.captureException(exception);
}
}
Panggil di ujung catch() tiap custom filter:
reportIfUnexpected(exception, status);
Checklist: cari semua custom filter di project sebelum anggap setup selesai:
grep -rln "@Catch(" src/ --include="*.ts" | grep -v node_modules
8. Scrub data sensitif (opsional tapi disarankan)
Karena sendDefaultPii: true bikin request body ikut terkirim penuh, tambahin
beforeSend di instrument.ts buat mask field sensitif:
Sentry.init({
// ...config di atas
beforeSend(event) {
const sensitiveFields = ['password', 'token', 'secret'];
if (event.request?.data) {
for (const field of sensitiveFields) {
if (event.request.data[field]) event.request.data[field] = '[FILTERED]';
}
}
return event;
},
});
9. Testing
Penting: SentryGlobalFilter tidak mengirim HttpException bawaan NestJS
(NotFoundException, BadRequestException, dll) secara default — itu dianggap
flow kontrol normal, bukan bug. Buat testing, trigger error yang beneran
unhandled, misal lewat constraint DB (contoh Prisma create() dengan data yang
melanggar unique constraint), bukan throw new HttpException(...).
Cara cepat verifikasi:
- Trigger error via endpoint yang ada request body/query params
- Cek dashboard → tab Issues → buka issue-nya
- Pastikan section Request ada: Body, Query String, Headers, IP
10. Troubleshooting
| Gejala | Kemungkinan penyebab |
|---|---|
Cannot find module './instrument.ts' |
Ada .ts literal di baris import main.ts, atau dist/ lama ke-cache — hapus dist dan build ulang |
Cannot find module './instrument' padahal sudah tanpa .ts |
File instrument.ts gak ada di src/, atau salah folder |
No DSN provided, client will not send events (pakai debug: true) |
.env belum ke-load pas Sentry.init() jalan — tambahin import 'dotenv/config' di baris pertama instrument.ts |
Event gak muncul di dashboard padahal Sentry Logger bilang "Captured error event" |
Cek log container GlitchTip (docker compose logs glitchtip-web) barengan waktu trigger — kalau gak ada request masuk sama sekali, cek konektivitas network / DSN salah domain |
| Request body / query params kosong di dashboard | Konfigurasi requestDataIntegration secara eksplisit (lihat bagian 4), jangan andelin default SDK |
| Error yang "seharusnya" muncul tapi gak ada | Kemungkinan ketangkep custom exception filter duluan — lihat bagian 7 |
Referensi
- Dokumentasi resmi Sentry untuk NestJS: https://docs.sentry.io/platforms/javascript/guides/nestjs/