Interceptors
Parte de la sección TypeScript y NestJS del Journey de Node.js de Coddy. Lección 23 de 24.
Quieres que cada respuesta indique cuánto tardó, sin tener que tocar cada controlador. Un interceptor envuelve al controlador: el código que se ejecuta antes de next.handle() se ejecuta antes, y el código que se ejecuta sobre lo que devuelve next.handle() se ejecuta después.
intercept(context: ExecutionContext, next: CallHandler) {
console.log('before');
return next.handle().pipe(
tap(() => console.log('after')),
);
}next.handle() proporciona el resultado del controlador como un observable de RxJS, un valor que llega más tarde. .pipe(tap(...)) ejecuta una función cuando llega el valor, antes de que Nest lo envíe; este pipe no tiene nada que ver con las tuberías de validación de Nest. Los guards se ejecutan antes que cualquier interceptor, así que una solicitud que un guard rechaza nunca llega al interceptor. Devuelve el observable encadenado: Nest envía lo que contiene. context.switchToHttp().getResponse() proporciona res, cuyo setHeader añade una cabecera.
app.useGlobalInterceptors(...) aplica un interceptor a todas las rutas; @UseInterceptors(...), a un solo controlador.
Desafío
Fácil«Ejecutar» vuelve a iniciar tu rastreador usando src/main.ts. Los archivos de práctica separados pueden permanecer en el árbol; este servidor no los inicia.
Los clientes y tu propio sistema de supervisión quieren saber cuánto tardó cada solicitud. Añade el encabezado de respuesta X-Response-Time a cada respuesta correcta, sin modificar ninguna ruta.
- Abre
src/common/response-time.interceptor.ts. En el métodointerceptdeResponseTimeInterceptor, primero toma nota de la hora actual conDate.now(). El código al principio deinterceptse ejecuta antes que el método de la ruta. - Obtén el
resde Express concontext.switchToHttp().getResponse(). next.handle()ejecuta el método de la ruta y devuelve su resultado como un observable de RxJS: un valor que llega más tarde. Sigue devolviéndolo, pero pásalo portap, que ya está importado desderxjs, usando el método.pipe()del observable. La función que le pasas atapse ejecuta cuando llega el resultado, antes de que Nest lo envíe. En esa función, llama ares.setHeaderpara establecerX-Response-Timeen los milisegundos transcurridos desde que tomaste nota de la hora, seguidos dems, como en3ms. (El método.pipe()de RxJS no tiene nada que ver con las tuberías de validación del capítulo 3. Añade un paso a la cadena del observable.)- En
src/main.ts, registra el interceptor para toda la aplicación conapp.useGlobalInterceptors(...), pasando una nueva instancia deResponseTimeInterceptor, junto a la tubería y el filtro. - Los cuerpos de las respuestas no deben cambiar.
GET /taskssigue devolviendo{ "items": [...], "page": 1, "total": 3, "pages": 2 }.
Pruébalo: envía GET /tasks y abre la pestaña Headers de la respuesta. Verás X-Response-Time con un valor como 0ms o 2ms. También aparece en GET /stats, y en un POST de { "title": "Buy milk" } enviado con el encabezado x-api-key: tasks-key-123. Después, envía ese POST sin la clave: 403 y sin X-Response-Time. Nest ejecuta los guardias antes que los interceptores, así que el guardia rechazó la solicitud antes de que se iniciara tu interceptor. Un error lanzado dentro de una ruta también omite el encabezado, porque tap solo se ejecuta cuando llega un resultado.
Pruébalo tú mismo
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
// Registra la hora, deja que se ejecute el handler y luego establece X-Response-Time.
return next.handle();
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de TypeScript y NestJS
1Fundamentos de Nest
Clases y métodosHola, NestRegistro de un controlador4Guards, filtros e interceptores
GuardsPractica por tu cuenta: Playground de Express