From harness-claude
Implements NestJS Interceptors to transform responses, log execution times, add timeouts, cache results, and handle errors across routes.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Transform responses and add cross-cutting behavior with NestInterceptor and CallHandler
Implements NestJS guards and interceptors for authentication, authorization, logging, and request/response transformation. Covers CanActivate, ExecutionContext, and JWT patterns for cross-cutting concerns.
Provides NestJS patterns for modules, controllers, providers, DTO validation, guards, interceptors, config, and production TypeScript backends. Useful for structuring APIs, adding validation, and database integrations.
Guides NestJS API development with architecture, modules, DI, guards, interceptors, pipes, MongoDB/Mongoose integration, auth, DTOs, error handling, and production patterns.
Share bugs, ideas, or general feedback.
Transform responses and add cross-cutting behavior with NestInterceptor and CallHandler
NestInterceptor<T, R> and the intercept(context, next) method which returns an Observable:@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, { data: T }> {
intercept(context: ExecutionContext, next: CallHandler): Observable<{ data: T }> {
return next.handle().pipe(map((value) => ({ data: value })));
}
}
@UseInterceptors(TransformInterceptor) at class or method level, or globally:// Global — in main.ts (no DI)
app.useGlobalInterceptors(new TransformInterceptor());
// Global — with DI (preferred)
providers: [{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor }];
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const start = Date.now();
return next.handle().pipe(tap(() => console.log(`${Date.now() - start}ms`)));
}
}
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(_: ExecutionContext, next: CallHandler): Observable<unknown> {
return next.handle().pipe(timeout(5000));
}
}
return next
.handle()
.pipe(catchError((err) => throwError(() => new BadGatewayException(err.message))));
next.handle() is pre-handler logic. Code in the RxJS pipe AFTER next.handle() is post-handler logic.Interceptors are executed in the NestJS lifecycle after guards and before pipes. They wrap the handler call using RxJS Observables, which is what makes both pre- and post-execution logic possible from a single point.
RxJS operators to know:
map(fn) — transform the response valuetap(fn) — side-effect without transforming (logging, metrics)timeout(ms) — throw TimeoutError if the observable does not emit in timecatchError(fn) — handle errors in the response streammergeMap / switchMap — flatten async operationsInterceptors vs Middleware: Middleware runs before routing. Interceptors wrap the entire route handler and its result. Use interceptors when you need to inspect or transform the handler's return value.
Interceptors vs Filters: Exception filters catch thrown errors. Interceptors can intercept errors via catchError in the RxJS stream, but exception filters are the canonical place for error shaping. Use interceptors for response transformation, not error handling.
Order of execution: When multiple interceptors are applied, they execute in order (outermost wraps innermost), similar to middleware. @UseInterceptors(A, B) means A's pre-logic, then B's pre-logic, then handler, then B's post-logic, then A's post-logic.
Serialization: NestJS ships ClassSerializerInterceptor which runs class-transformer's instanceToPlain on every response, respecting @Exclude() and @Expose() decorators on entity classes.
https://docs.nestjs.com/interceptors