git clone https://github.com/Intense-Visions/harness-engineering
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/nestjs-interceptors-pattern" ~/.claude/skills/intense-visions-harness-engineering-nestjs-interceptors-pattern-633772 && rm -rf "$T"
agents/skills/codex/nestjs-interceptors-pattern/SKILL.mdNestJS Interceptors Pattern
Transform responses and add cross-cutting behavior with NestInterceptor and CallHandler
When to Use
- You need to transform every response from a controller into a consistent envelope format
- You need to log execution time for every route without touching each handler
- You need to add a response-level timeout that cancels slow database queries
- You need to cache responses or add response headers based on handler metadata
Instructions
- Implement
and theNestInterceptor<T, R>
method which returns anintercept(context, next)
: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 }))); } }
- Apply with
at class or method level, or globally:@UseInterceptors(TransformInterceptor)
// Global — in main.ts (no DI) app.useGlobalInterceptors(new TransformInterceptor()); // Global — with DI (preferred) providers: [{ provide: APP_INTERCEPTOR, useClass: TransformInterceptor }];
- Logging interceptor — measure execution time:
@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`))); } }
- Timeout interceptor — cancel after N milliseconds:
@Injectable() export class TimeoutInterceptor implements NestInterceptor { intercept(_: ExecutionContext, next: CallHandler): Observable<unknown> { return next.handle().pipe(timeout(5000)); } }
- Exception mapping — transform errors in the stream:
return next .handle() .pipe(catchError((err) => throwError(() => new BadGatewayException(err.message))));
- Code that runs BEFORE
is pre-handler logic. Code in the RxJS pipe AFTERnext.handle()
is post-handler logic.next.handle()
Details
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:
— transform the response valuemap(fn)
— side-effect without transforming (logging, metrics)tap(fn)
— throwtimeout(ms)
if the observable does not emit in timeTimeoutError
— handle errors in the response streamcatchError(fn)
/mergeMap
— flatten async operationsswitchMap
Interceptors 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.
Source
https://docs.nestjs.com/interceptors
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.