@wisemen/nestjs-cache
v0.1.0
Published
A NestJS module for caching, with two backends: a Redis-backed client (built on [redis](https://github.com/redis/node-redis)) and a basic in-memory client for local development or tests.
Downloads
535
Maintainers
Keywords
Readme
@wisemen/nestjs-cache
A NestJS module for caching, with two backends: a Redis-backed client (built on redis) and a basic in-memory client for local development or tests.
Installation
pnpm add @wisemen/nestjs-cacheThis package does not depend on redis at runtime unless you select the Redis backend: the
redis package is only ever imported when CacheProvider.REDIS is used, so an app that only uses
the in-memory backend does not need redis installed. If you use the Redis backend, install it in
your app:
pnpm add redisUsage
Setting up the module
Pass a provider to select the backend. CacheModule builds, connects and owns the client for
you.
Redis:
import { Module } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { captureException } from '@wisemen/opentelemetry'
import { CacheModule, CacheProvider } from '@wisemen/nestjs-cache'
@Module({
imports: [
CacheModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
provider: CacheProvider.REDIS,
config: {
url: config.getOrThrow('REDIS_URL'),
disableOfflineQueue: true
},
ttl: config.get('CACHE_DEFAULT_TTL'),
onClientError: (error) => {
captureException(error)
}
})
})
],
exports: [CacheModule]
})
export class DefaultCacheModule {}config is passed straight through to redis's createClient(), so any RedisClientOptions it
accepts works here. CacheModule creates the client, connects it, and routes its error events
through onClientError (or Logger.error by default) — your app does not create or manage the
Redis client itself.
In-memory (a basic process-local cache, handy for local development or tests that shouldn't
depend on a real Redis instance, and it does not need redis installed):
CacheModule.forRoot({ provider: CacheProvider.IN_MEMORY })Pass ttl to change the default expiry:
CacheModule.forRoot({ provider: CacheProvider.IN_MEMORY, ttl: 60 })Options
| Option | Backend | Description |
| --------------- | ------- | --------------------------------------------------------------------------------------- |
| provider | Both | CacheProvider.REDIS or CacheProvider.IN_MEMORY. Selects the backend. |
| config | Redis | RedisClientOptions, passed straight through to redis's createClient(). |
| onClientError | Redis | Called with errors from read commands and the client's error event. Defaults to Logger.error. |
| ttl | Both | Default expiry in seconds. Defaults to DEFAULT_CACHE_TTL_SECONDS. |
Creating a cache service
Extend ScopedCache to build namespaced cache services, and inject the abstract Cache so the
service works with whichever backend the module was configured with.
import { Injectable } from '@nestjs/common'
import { Cache, ScopedCache } from '@wisemen/nestjs-cache'
@Injectable()
export class RoleCache extends ScopedCache {
override readonly prefix = 'role'
constructor (private readonly cache: Cache) {
super()
}
async getRole (id: string): Promise<string | null> {
return await this.cache.getValue<string>(this.buildCacheKey(id))
}
async setRole (id: string, value: string): Promise<void> {
await this.cache.putValue(this.buildCacheKey(id), value)
}
}Raw Redis access
Cache only exposes cache-oriented methods. When the Redis backend is used, the injected Cache
is a RedisCacheClient (exported as a type) whose client property is the underlying redis
client. Use it to share the connection with other modules, for example the throttler storage.
ThrottlerModule.forRootAsync({
inject: [Cache],
useFactory: (cache: Cache) => ({
throttlers: [{ ttl: 60_000, limit: 10 }],
storage: isRedisConfigured()
? new RedisThrottlerStorage((cache as RedisCacheClient).client)
: undefined
})
})Use a isRedisConfigured check (e.g. whether the Redis env vars are set) to choose the cache
provider and the throttler storage. That way the cast is only reached when CacheProvider.REDIS is
active, and without Redis the throttler falls back to its default in-memory storage. The cache owns
the connection and closes it on shutdown, so the storage must not close the client itself.
Registering a cache module
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { DefaultCacheModule } from './default-cache.module.js'
import { Role } from './role.entity.js'
import { RoleCache } from './role.cache.js'
@Module({
imports: [
DefaultCacheModule,
TypeOrmModule.forFeature([Role])
],
providers: [RoleCache],
exports: [RoleCache]
})
export class RoleCacheModule {}