FORMA

缓存

@nestjs/cache-manager 统一缓存 API,存储实现可插拔。

NestJS 的缓存模块是通过 @nestjs/cache-manager 包来实现的,它为应用带来了一个简单统一的 API,底层支持多种性能出色的缓存存储方案,并可以灵活配置。

模块导入与基础配置

要开始使用缓存,首先需要安装 @nestjs/cache-manager 核心包和 cache-manager 存储驱动:

bash
npm install @nestjs/cache-manager cache-manager

安装完成后,在模块(例如 AppModule)中通过 .register() 方法导入并配置 CacheModule,最基础的内存缓存开启方式如下:

typescript
// app.module.ts
import { Module } from "@nestjs/common";
import { CacheModule } from "@nestjs/cache-manager";

@Module({
  imports: [
    CacheModule.register({
      ttl: 60000, // 设置全局缓存过期时间为 60 秒(单位:毫秒)
      max: 100, // 设置内存缓存中最大存储的项数为 100
      isGlobal: true, // 设为全局,避免在每个模块重复导入
    }),
  ],
})
export class AppModule {}

⚠️ 特别提醒:在使用 cache-manager v5 或更高版本时,ttl 的单位是毫秒 (milliseconds), 而旧版 v4 使用的是秒 (seconds)。需要仔细核对所使用的库版本。

💾 存储引擎:内存 vs. Redis

@nestjs/cache-manager 默认提供内存缓存,它可以直接将数据存储在应用进程中,适用于单机或中小规模的场景。但对于需要跨实例共享缓存的分布式系统,则必须选择像 Redis 这样的集中式存储。

使用 Redis 时,需要额外安装驱动包并调整配置:

bash
npm install cache-manager-redis-yet

然后在 CacheModule 中配置 store 和连接参数:

typescript
// app.module.ts
import { Module } from "@nestjs/common";
import { CacheModule } from "@nestjs/cache-manager";
import { redisStore } from "cache-manager-redis-yet";

@Module({
  imports: [
    CacheModule.registerAsync({
      useFactory: async () => ({
        store: await redisStore({
          socket: {
            host: "localhost",
            port: 6379,
          },
          password: "your-password",
          ttl: 60000, // 基于 Redis 的 TTL
        }),
      }),
    }),
  ],
})
export class AppModule {}

🛠️ 与缓存实例交互

在某些复杂场景下,你可能需要直接操作缓存。通过 CACHE_MANAGER 这个 token 就可以注入 Cache 实例,进而对缓存进行精细化的控制。

typescript
// your.service.ts
import { Injectable, Inject } from "@nestjs/common";
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Cache } from "cache-manager";

@Injectable()
export class YourService {
  constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}

  async getValue(key: string) {
    // 获取缓存,若不存在则返回 null
    const value = await this.cacheManager.get(key);
    // 手动设置缓存,第三个参数可指定该键的 TTL(单位:毫秒)
    await this.cacheManager.set(key, { value: "cached data" }, 1000);
    // 删除单个键
    await this.cacheManager.del(key);
    // 清空整个缓存(请谨慎使用)
    await this.cacheManager.reset();
  }
}

🧩 自动缓存响应 (CacheInterceptor)

若只需对 GET 请求进行缓存,使用 CacheInterceptor 是最简洁的方案。它将自动拦截响应并将其存入缓存。你可以将其应用到控制器层(作用于所有路由)或方法层。

typescript
// app.controller.ts
import { Controller, Get, UseInterceptors } from "@nestjs/common";
import { CacheInterceptor } from "@nestjs/cache-manager";

@Controller()
@UseInterceptors(CacheInterceptor)
export class AppController {
  @Get()
  findAll(): string[] {
    return []; // 此响应将被自动缓存
  }
}

🎨 自定义缓存键 (CacheKey) 与 TTL (CacheTTL)

CacheInterceptor 默认会根据路由生成缓存键,例如 GET /users。如果需要更精细的控制,可以使用 @CacheKey()@CacheTTL() 装饰器。

typescript
import { CacheKey, CacheTTL } from '@nestjs/cache-manager';

@Get()
@CacheKey('custom_key_for_findAll')
@CacheTTL(120000)  // 覆盖全局设置,将此接口的缓存时间设为 120 秒
findAll(): string[] {
  return [];
}

🌐 全局缓存配置

如果你希望缓存拦截器默认作用于所有路由,可以在模块配置中将其声明为全局拦截器,但也需要注意对 WebSocket 或非 GET 请求等场景的处理。

typescript
// app.module.ts
import { Module } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { CacheInterceptor } from "@nestjs/cache-manager";

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: CacheInterceptor,
    },
  ],
})
export class AppModule {}

高级用法与最佳实践

  1. 缓存一致性:当数据变更(如数据库的增、删、改操作)时,确保同时删除或更新对应的缓存。一种常见的模式是在 @Put(), @Post(), @Delete() 等会修改数据的操作中,调用 this.cacheManager.del(key)
  2. 访问控制:避免在需要验证用户权限、需要返回动态内容(如需要 @Res() 直接操作响应)、或 GraphQL 解析器等场景中使用 CacheInterceptor
  3. 数据类型限制:内存缓存只能存储符合 结构化克隆算法 支持的类型,如基本类型和普通对象。这意味着像 Date 对象、SetMap 等高级对象的缓存可能会遇到问题。

总结

核心概念实现关键点
模块注册使用 CacheModule.register() 进行基础配置;使用 CacheModule.registerAsync() 实现更灵活的动态配置。
存储选择默认内存,适合开发;上生产或分布式场景需用 Redis,推荐 cache-manager-redis-yet 驱动。
自动缓存通过 @UseInterceptors(CacheInterceptor) 实现,简单高效。
精细控制通过 @CacheKey()@CacheTTL() 自定义键值与过期时间,满足特殊场景需求。
手动控制通过 @Inject(CACHE_MANAGER) 获得 Cache 实例,实现更灵活的存取、删除等精细操作。
最佳实践:缓存失效在数据更新时务必同步删除或更新缓存,以保障数据一致性。

希望这份介绍能帮助你系统了解并快速上手 NestJS 的缓存功能。如果希望继续了解某个部分更具体的用法,也可以告诉我~

参考文献

以下链接在编写时均可正常访问:

资料说明
NestJS 文档官方
Caching本章主题
Request lifecycle执行顺序