Reputation: 483
I need to do event tracking for certain API calls(based on handler name) and it is basically a function that records the activity.
At the moment I have a interceptor that does this event tracking and it works fine.
But problem is whenever there is an error, i am catching it using a global exception filter AND this returns the response immediately without entering the interceptor and my event tracking code(I need the class name, handler name and returned data if available).
Here is my setup
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';
import { AuthModule } from './auth/auth.module';
import { MembersModule } from './members/members.module';
import { DatabaseConfigService } from './_database/database-config.service';
import { EventTrackerService } from './event-tracker/event-tracker.service';
import { LoggingInterceptor } from './_interceptors/logging.interceptor';
import { EventsTrackerInterceptor } from './_interceptors/events-tracker.interceptor';
import { AllExceptionsFilter } from './_filters/all-exception.filter';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
MongooseModule.forRootAsync({
useClass: DatabaseConfigService,
}),
AuthModule,
MembersModule,
],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
{
provide: APP_INTERCEPTOR,
useClass: EventsTrackerInterceptor,
},
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
EventTrackerService,
],
})
export class AppModule {}
events-tracker.interceptor.ts
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { EventTrackerService } from '../event-tracker/event-tracker.service';
@Injectable()
export class EventsTrackerInterceptor implements NestInterceptor {
constructor(private readonly eventTrackerService: EventTrackerService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
const className = context.getClass().name;
const handler = context.getHandler().name;
return next.handle().pipe(
tap(data => {
this.eventTrackerService.handleEvent(
request.url,
className,
handler,
data,
);
}),
);
}
}
all-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { EventTrackerService } from '../event-tracker/event-tracker.service';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly eventTrackerService: EventTrackerService) {}
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.FORBIDDEN;
const message =
typeof exception.message === 'string'
? exception.message
: exception.message.message;
const errorResponse = {
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: message || 'Something went wrong',
};
Logger.error(
`${request.method} ${request.url} ${status}`,
JSON.stringify(errorResponse),
'AllExceptionsFilter',
true,
);
response.status(status).json(errorResponse);
}
}
My ultimate question is: How do I call the events tracker in interceptor despite the exception being throw?
Upvotes: 2
Views: 4469
Reputation: 70131
You have one of two options with the interceptor: 1) implement a catchError
branch of the pipe
, do your logic and re-throw the error, or 2) tap
can take in an object similar to subscribe for next, error, complete
and run certain functions for what happens with the observable. The tap could look like
return next.handle().pipe(
tap({
next: (data) => this.eventTrackerService.handleEvent(
request.url,
className,
handler,
data,
),
error: (err) => this.eventTrackerService.handleEvent(
request.url,
className,
handler,
error,
)
})
);
And now you have a tap
that works on success and error, but now you're also passing the error instead of what data was to be returning to your tracker.
Upvotes: 5