Implementación de detección de versión basada en encabezados en NestJS: Usando interceptores
- 828Palabras
- 4Minutos
- 14 Sep, 2024
En un proyecto reciente, necesitábamos agregar control de versiones a la API para asegurarnos de que diferentes versiones de clientes fueran compatibles con los cambios de la API, y al mismo tiempo, poder notificar a los clientes de versiones antiguas que actualicen cuando sea necesario. Este tipo de necesidad es común en el desarrollo móvil, donde los clientes suelen enviar el número de versión actual a través del encabezado de la solicitud HTTP, y el servidor devuelve la respuesta correspondiente según el número de versión.
El problema que enfrentamos era que, en algunos casos, cuando la versión del cliente era demasiado antigua, queríamos poder adjuntar información sobre la actualización en la respuesta normal, de modo que esta lógica pudiera manejarse uniformemente en el lado del cliente sin necesidad de modificar la lógica de negocio existente. Para resolver este problema, podemos optar por usar interceptores o middleware en NestJS.
Este artículo toma como ejemplo el uso de interceptores para implementar esta necesidad. Para la implementación del middleware, puede consultar este artículo: 《Implementación de detección de versión basada en encabezados en NestJS: Usando middleware》
Análisis de la necesidad
La necesidad es simple: en cada solicitud, el encabezado debe incluir el número de versión del cliente, y el servidor debe verificar este número. Si la versión del cliente es inferior a la requerida por el servidor, se debe adjuntar un campo de notificación de actualización en la respuesta; si la versión es adecuada, se devuelven los datos normales sin modificaciones.
Cómo implementar
En NestJS, podemos usar interceptores para capturar las solicitudes y respuestas. En el interceptor podemos:
- Obtener la información de la versión del encabezado.
- Determinar si se necesita actualización, mediante una simple comparación de versiones.
- Modificar los datos de respuesta, si se necesita actualizar, agregando un campo adicional a los datos devueltos.
1. Crear un interceptor de detección de versiones
Los interceptores son herramientas importantes en NestJS para interceptar solicitudes y respuestas. En este caso, necesitamos crear un interceptor que se encargue de la detección y manipulación de la versión antes de que los datos de respuesta se envíen al cliente.
1import {2 CallHandler,3 ExecutionContext,4 Injectable,5 NestInterceptor,6} from "@nestjs/common";7import { Observable } from "rxjs";8import { map } from "rxjs/operators";9
10@Injectable()11export class VersionInterceptor implements NestInterceptor {12 private readonly latestVersion = "2.0.0"; // Establecer la última versión13
14 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {15 const request = context.switchToHttp().getRequest();16 const version = request.headers["version"]; // Obtener la versión del encabezado17
18 return next.handle().pipe(19 map((data) => {20 if (version && this.needsUpdate(version)) {21 // Si necesita actualización, agregar un campo adicional22 return {23 ...data,24 updateAvailable: true,25 updateUrl: "xxxx", // URL de actualización26 latestVersion: this.latestVersion,27 message: "Una nueva versión está disponible, por favor actualice.",28 };29 }30 // No se necesita actualización, devolver los datos originales31 return data;32 }),33 );34 }35
36 // Lógica de comparación de versiones37 private needsUpdate(clientVersion: string): boolean {38 return clientVersion < this.latestVersion;39 }40}
En este interceptor, obtenemos la versión enviada por el cliente a través de request.headers['version']
y llamamos al método needsUpdate
para realizar la comparación de versiones. Si la versión del cliente es inferior, insertamos el campo adicional updateAvailable
en los datos devueltos para informar al usuario que hay una nueva versión disponible.
2. Aplicar el interceptor
Hay dos formas de aplicar este interceptor en NestJS: aplicación local o global.
Aplicación local del interceptor
Si solo necesitas usar la detección de versiones en algunas rutas específicas, puedes aplicar el interceptor en el controlador correspondiente.
1import { Controller, Get, UseInterceptors } from "@nestjs/common";2import { VersionInterceptor } from "./version.interceptor";3
4@Controller("api")5export class AppController {6 @Get("data")7 @UseInterceptors(VersionInterceptor)8 getData() {9 return {10 data: "Aquí están tus datos",11 };12 }13}
Aplicación global del interceptor
Si deseas que todas las API admitan la detección de versiones, puedes aplicar el interceptor de forma global en main.ts
.
1import { NestFactory } from "@nestjs/core";2import { AppModule } from "./app.module";3import { VersionInterceptor } from "./version.interceptor";4
5async function bootstrap() {6 const app = await NestFactory.create(AppModule);7
8 // Usar el interceptor globalmente9 app.useGlobalInterceptors(new VersionInterceptor());10
11 await app.listen(3000);12}13bootstrap();
3. Probar la API
Después de configurar el interceptor, podemos probar la API usando curl
o Postman. El cliente envía la información de la versión a través del encabezado de la solicitud, y el servidor devolverá la respuesta correspondiente según el número de versión.
Ejemplo de solicitud
1curl -X GET http://localhost:3000/api/data -H "version: 1.0.0"
Ejemplo de respuesta (cuando se necesita actualización)
1{2 "data": "Aquí están tus datos",3 "updateAvailable": true,4 "latestVersion": "2.0.0",5 "message": "Una nueva versión está disponible, por favor actualice."6}
Ejemplo de respuesta (cuando no se necesita actualización)
1{2 "data": "Aquí están tus datos"3}
Conclusión
Los interceptores son una herramienta muy eficiente y flexible para manejar la detección de versiones, permitiéndonos modificar dinámicamente los datos de respuesta sin cambiar la lógica del controlador. Este enfoque no solo es útil para el control de versiones, sino también para otros escenarios similares, como la formateación global de contenido de respuesta o la adición de metadatos adicionales.
Este método nos ayuda a mantener la compatibilidad de la API y garantiza que los clientes de versiones antiguas reciban avisos de actualización oportunamente, mejorando la experiencia del usuario.