Skip to content

Commit 58848c1

Browse files
authored
feat: integrate Sentry for error tracking and fix metrics DI startup error (#55)
1 parent cb60433 commit 58848c1

8 files changed

Lines changed: 110 additions & 10 deletions

File tree

.env.example

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Application
2+
NODE_ENV=development
3+
PORT=3000
4+
API_URL=http://localhost:3000
5+
6+
# Database
7+
SUPABASE_URL=https://your-project.supabase.co
8+
SUPABASE_ANON_KEY=your-anon-key
9+
SUPABASE_SERVICE_KEY=your-service-role-key
10+
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
11+
DATABASE_URL=postgresql://postgres:password@db.your-project.supabase.co:5432/postgres
12+
13+
# Stellar
14+
STELLAR_NETWORK=testnet
15+
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
16+
STELLAR_SOROBAN_URL=https://soroban-testnet.stellar.org
17+
STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
18+
19+
# Smart Contract IDs (deployed smart contracts)
20+
REPUTATION_CONTRACT_ID=
21+
CREDITLINE_CONTRACT_ID=
22+
MERCHANT_REGISTRY_CONTRACT_ID=
23+
LIQUIDITY_POOL_CONTRACT_ID=
24+
25+
# Auth
26+
JWT_SECRET=
27+
JWT_ACCESS_EXPIRATION=15m
28+
JWT_REFRESH_EXPIRATION=7d
29+
NONCE_EXPIRATION=300
30+
31+
# Redis
32+
REDIS_URL=redis://localhost:6379
33+
REDIS_DB=0
34+
REPUTATION_CACHE_TTL=300
35+
36+
# Logging
37+
LOG_LEVEL=debug
38+
LOG_PRETTY=true
39+
40+
# CORS
41+
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
42+
CORS_CREDENTIALS=true
43+
44+
# Jobs
45+
JOBS_ENABLED=true
46+
INDEXER_INTERVAL=30
47+
TX_STATUS_INTERVAL=15
48+
REMINDER_CRON=0 9 * * *
49+
50+
# Sentry
51+
SENTRY_DSN=
52+
SENTRY_TRACES_SAMPLE_RATE=0.1

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ The bootstrap command will guide you through the process of setting up a Supabas
165165
| **Swagger Docs** | https://stepfi-api.onrender.com/api/v1/docs |
166166
| **Health Check** | https://stepfi-api.onrender.com/api/v1/health |
167167

168+
## 🛠️ Error tracking
169+
170+
Error tracking is powered by Sentry. To enable error tracking in development or production:
171+
172+
1. Create a Sentry project for Nest.js.
173+
2. Add the following environment variables to your `.env` file:
174+
```env
175+
# Sentry DSN for error reporting
176+
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
177+
178+
# Optional: Sentry Traces Sample Rate (defaults to 0.1)
179+
SENTRY_TRACES_SAMPLE_RATE=0.1
180+
```
181+
3. In production, unhandled exceptions will automatically be reported to Sentry. If `SENTRY_DSN` is not provided, the Sentry SDK will operate in no-op mode (silently) and the app will log unhandled exceptions only to stdout.
182+
168183
## Docs
169184

170185
Command & config reference can be found [here](https://supabase.com/docs/reference/cli/about).

context/progress-tracker.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ EAS build for Expo preview → then landing page → then GitHub issues → then
5050
- `node_modules` cached via `actions/cache@v4` keyed on `package-lock.json` hash
5151
- CI status badge added to `README.md` pointing at the workflow
5252

53+
### Error Tracking
54+
- Integrated `@sentry/nestjs` into the API orchestrator.
55+
- Configured Sentry initialization at the very top of `src/main.ts` before NestJS bootstrap.
56+
- Registered `SentryModule` and global exception filter `SentryGlobalFilter` in `src/app.module.ts`.
57+
- Created `.env.example` with template environment variables, including `SENTRY_DSN` and `SENTRY_TRACES_SAMPLE_RATE`.
58+
- Added `sentry-test` endpoint to `HealthController` for verification.
59+
- Documented Sentry configuration in `README.md`.
60+
5361
---
5462

5563
## In Progress

src/app.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
2-
import { APP_GUARD } from '@nestjs/core';
2+
import { APP_GUARD, APP_FILTER } from '@nestjs/core';
33
import { ConfigModule, ConfigService } from '@nestjs/config';
44
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
55
import { BullModule } from '@nestjs/bullmq';
6+
import { SentryModule, SentryGlobalFilter } from '@sentry/nestjs/setup';
67
import { AuthModule } from './modules/auth/auth.module';
78
import { HealthModule } from './modules/health/health.module';
89
import { LoansModule } from './modules/loans/loans.module';
@@ -29,6 +30,7 @@ import { CorrelationIdMiddleware } from './common/logger/correlation-id.middlewa
2930
@Module({
3031
imports: [
3132
ConfigModule.forRoot({ isGlobal: true }),
33+
SentryModule.forRoot(),
3234
LoggerModule,
3335
ThrottlerModule.forRoot([
3436
{
@@ -72,6 +74,10 @@ import { CorrelationIdMiddleware } from './common/logger/correlation-id.middlewa
7274
provide: APP_GUARD,
7375
useClass: ThrottlerGuard,
7476
},
77+
{
78+
provide: APP_FILTER,
79+
useClass: SentryGlobalFilter,
80+
},
7581
],
7682
})
7783
export class AppModule implements NestModule {

src/main.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import * as Sentry from '@sentry/nestjs';
2+
3+
Sentry.init({
4+
dsn: process.env.SENTRY_DSN || undefined,
5+
environment: process.env.NODE_ENV || 'development',
6+
tracesSampleRate: process.env.SENTRY_TRACES_SAMPLE_RATE
7+
? parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE)
8+
: 0.1,
9+
});
10+
111
import { ValidationPipe } from '@nestjs/common';
212
import { NestFactory } from '@nestjs/core';
313
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';

src/modules/health/health.controller.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,11 @@ export class HealthController {
2323
async checkDatabase() {
2424
return this.healthService.checkDatabaseMinimal();
2525
}
26+
27+
@Get('sentry-test')
28+
@ApiOperation({ summary: 'Trigger a deliberate error to verify Sentry integration' })
29+
@ApiResponse({ status: 500, description: 'Sentry test error triggered successfully' })
30+
async triggerSentryTest() {
31+
throw new Error('sentry test');
32+
}
2633
}

src/modules/metrics/metrics.module.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Module } from '@nestjs/common';
1+
import { Module, Global } from '@nestjs/common';
22
import { APP_INTERCEPTOR } from '@nestjs/core';
33
import { BullModule } from '@nestjs/bullmq';
44
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
@@ -8,6 +8,7 @@ import { MetricsInterceptor } from './metrics.interceptor';
88
import { MetricsUpdater } from './metrics.updater';
99
import { SupabaseService } from '../../database/supabase.client';
1010

11+
@Global()
1112
@Module({
1213
imports: [
1314
PrometheusModule.register({
@@ -23,7 +24,7 @@ import { SupabaseService } from '../../database/supabase.client';
2324
{ name: 'nonce-cleanup' },
2425
),
2526
],
26-
controllers: [MetricsController],
27+
controllers: [],
2728
providers: [
2829
...metricProviders,
2930
MetricsService,

src/modules/metrics/metrics.service.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Inject, Injectable } from '@nestjs/common';
1+
import { Injectable } from '@nestjs/common';
22
import { Counter, Gauge, Histogram } from 'prom-client';
33
import {
4+
InjectMetric,
45
makeCounterProvider,
56
makeGaugeProvider,
67
makeHistogramProvider,
@@ -47,17 +48,17 @@ export const metricProviders = [
4748
@Injectable()
4849
export class MetricsService {
4950
constructor(
50-
@Inject(HTTP_REQUEST_COUNT)
51+
@InjectMetric(HTTP_REQUEST_COUNT)
5152
private readonly requestCounter: Counter<string>,
52-
@Inject(HTTP_REQUEST_DURATION_SECONDS)
53+
@InjectMetric(HTTP_REQUEST_DURATION_SECONDS)
5354
private readonly requestDuration: Histogram<string>,
54-
@Inject(BULLMQ_QUEUE_DEPTH)
55+
@InjectMetric(BULLMQ_QUEUE_DEPTH)
5556
private readonly queueDepth: Gauge<string>,
56-
@Inject(INDEXER_LAG)
57+
@InjectMetric(INDEXER_LAG)
5758
private readonly indexerLag: Gauge<string>,
58-
@Inject(HORIZON_HEALTH)
59+
@InjectMetric(HORIZON_HEALTH)
5960
private readonly horizonHealth: Gauge<string>,
60-
@Inject(DB_POOL_OPEN)
61+
@InjectMetric(DB_POOL_OPEN)
6162
private readonly dbPoolOpen: Gauge<string>,
6263
) {}
6364

0 commit comments

Comments
 (0)