El almacenamiento en caché puede ser un problema para cualquier desarrollador. He pasado demasiadas horas lidiando con API lentas y bases de datos sobrecargadas, buscando una solución eficaz y fácil de implementar.
Por eso me emocioné cuando Karol ( karol71927 ), un talentoso miembro de nuestra organización de código abierto, Nestixis , creó @nestixis/cache-manager.
Esta biblioteca liviana, impulsada por Redis, ha simplificado el almacenamiento en caché en mis proyectos NestJS y estoy ansioso por compartir cómo marcó la diferencia.
El desafío: la complejidad del almacenamiento en caché en NestJS
El escenario es muy familiar: tu aplicación funciona sin problemas hasta que se dispara el tráfico y, de repente, tu base de datos colapsa por la carga. El almacenamiento en caché es la solución obvia: almacenar los datos una vez y servirlos rápidamente, pero integrarlo en NestJS suele resultar engorroso. Redis ofrece potentes funciones, pero configurarlo suele implicar gestionar configuraciones, gestionar políticas de expiración y definir claves de caché personalizadas.
Necesitaba una herramienta que simplificara el proceso y al mismo tiempo permitiera un control preciso sobre lo que se almacena en caché, como parámetros de solicitud o consultas.
Por ello, Karol diseñó @nestixis/cache-manager para abordar estos problemas con un enfoque limpio y eficiente. Este paquete proporciona una API sencilla para gestionar el almacenamiento en caché de Redis, con TTL configurables y compatibilidad con estrategias avanzadas de almacenamiento en caché. Está disponible en su repositorio de GitHub y su diseño refleja el compromiso de nuestro equipo con herramientas prácticas y reutilizables.
Primeros pasos: configuración perfecta
La instalación es lo más sencilla posible:
npm i @nestixis/cache-manager
Para integrarlo en su aplicación NestJS, regístrelo en un módulo:
import { Module } from '@nestjs/common'; import { CacheModule } from '@nestixis/cache-manager'; import { ConfigModule, ConfigService } from '@nestjs/config'; @Module({ imports: [ CacheModule.registerAsync({ isGlobal: true, imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ redis: { host: configService.get('REDIS_HOST') || 'localhost', port: +configService.get('REDIS_PORT') || 6379, }, cachePrefix: 'cache:', defaultCacheTTL: 1000, // 1-second default }), inject: [ConfigService], }), ], }) export class AppModule {}
Esto configura el almacenamiento en caché de Redis en toda tu aplicación con un mínimo esfuerzo. Desde ahí, puedes interactuar con él manualmente en un servicio:
import { Injectable } from '@nestjs/common'; import { CacheManager } from '@nestixis/cache-manager'; @Injectable() export class MyService { constructor(private readonly cacheManager: CacheManager) {} async getData(key: string) { const cached = await this.cacheManager.get(key); if (cached) return cached; const data = await this.fetchData(); await this.cacheManager.add(key, data, 1000); // Cache for 1 second return data; } async clearData(key: string) { await this.cacheManager.remove(key); } private fetchData() { return new Promise((resolve) => setTimeout(() => resolve('Data'), 2000)); } }
Lo que distingue a este paquete es su capacidad de almacenar en caché según los detalles específicos de la solicitud, una función que Karol incluyó cuidadosamente.
controlador:
import { Controller, Get, Post, Delete, Param, UseInterceptors } from '@nestjs/common'; import { CacheInterceptor, CacheRemoveInterceptor, CacheTrackBy } from '@nestixis/cache-manager'; import { MyService } from './my.service'; @Controller('site/:token') @CacheTrackBy({ prefix: 'site', ttl: 10000, // 10 seconds by: [ { by: 'param', name: 'token', }, ], }) export class SiteController { constructor(private readonly service: MyService) {} @Get() @UseInterceptors(CacheInterceptor) async get(@Param('token') token: string) { return this.service.getData(`site:${token}`); } // Will clear cache on add or remove of resource to keep fresh state @Post() @UseInterceptors(CacheRemoveInterceptor) async add(@Param('token') token: string) { await this.service.getData(`site:${token}`); // Refresh data } @Delete() @UseInterceptors(CacheRemoveInterceptor) async remove(@Param('token') token: string) { await this.service.clearData(`site:${token}`); } }
El decorador @CacheTrackBy
es clave. Garantiza que el almacenamiento en caché esté vinculado al parámetro :token, de modo que /site/abc y /site/xyz tengan su propia entrada de caché.
Puedes ajustarlo para usar consultas u otros criterios, lo que ofrece la flexibilidad que siempre había deseado. CacheInterceptor gestiona las solicitudes GET, mientras que CacheRemoveInterceptor borra la caché al actualizar: un método elegante e intuitivo.