@nest-mods/use-cache
v0.1.1
Published
Method-result caching utilities for NestJS.
Readme
@nest-mods/use-cache
Method-result caching for NestJS 11, built on
@nestjs/cache-manager.
It provides:
- a global, Redis-backed cache module with synchronous and asynchronous registration;
- a
UseCache()method decorator with configurable keys, filters, and TTLs; - direct cache access and invalidation through
CacheService; - readable TTL values such as
'5 seconds', with numeric TTLs in milliseconds; - native ESM and CommonJS package entry points.
Requirements
- Node.js 24 or newer
- NestJS 11
@nestjs/cache-manager3cache-manager7- a reachable Redis server
Installation
npm install @nest-mods/use-cacheThe package installs @nestjs/cache-manager, cache-manager, and its Redis
adapter directly. Nest applications normally already provide the peer
dependencies: @nestjs/common, @nestjs/core, and reflect-metadata.
Configuration
Import UseCacheModule once in the root application module and give its Redis
keys a non-empty namespace:
import { Module } from '@nestjs/common';
import { UseCacheModule } from '@nest-mods/use-cache';
@Module({
imports: [UseCacheModule.forRoot({ namespace: 'my-api:cache' })],
})
export class AppModule {}The package creates and owns one Redis store. Applications configure only the connection and cache namespace:
import { Module } from '@nestjs/common';
import { UseCacheModule } from '@nest-mods/use-cache';
@Module({
imports: [
UseCacheModule.forRoot({
host: process.env.REDIS_HOST,
port: Number(process.env.REDIS_PORT ?? 6379),
namespace: 'my-api:cache',
ttl: '30 seconds',
}),
],
})
export class AppModule {}namespace is required. Blank values and Redis glob metacharacters (*, ?,
[, ], and \) are rejected so namespace-scoped resets cannot match
neighboring prefixes. host defaults to 127.0.0.1, port defaults to 6379,
and ttl defaults to 5 seconds. Numeric TTL values are milliseconds.
@UseCache() and CacheService.cacheSet() inherit this TTL when they do not
provide an override.
UseCacheModule is always global. Store construction, the ':' key prefix
separator, and unlink-based deletion are internal details and are not public
configuration options.
Closing the Nest application with await app.close() disconnects stores owned
by the underlying Nest cache module. Enabling process signal hooks remains the
responsibility of the consuming application.
Asynchronous registration
forRootAsync() accepts Nest imports and inject, while its factory returns
the same simplified Redis options. This example uses @nestjs/config, but this
package does not depend on it or assume a specific configuration key:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { UseCacheModule } from '@nest-mods/use-cache';
@Module({
imports: [
ConfigModule.forRoot(),
UseCacheModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
host: config.get<string>('REDIS_HOST', '127.0.0.1'),
port: config.get<number>('REDIS_PORT', 6379),
namespace: config.getOrThrow<string>('CACHE_NAMESPACE'),
ttl: config.get<number>('CACHE_TTL_MS', 5_000),
}),
}),
],
})
export class AppModule {}The factory may return the options directly or return a promise.
Usage
Cache a method result
UseCache() decorates asynchronous methods:
import { Injectable } from '@nestjs/common';
import { UseCache } from '@nest-mods/use-cache';
@Injectable()
export class CatalogService {
@UseCache()
async findOne(id: string): Promise<Product> {
return this.products.findById(id);
}
}The default key is:
ClassName#methodName:JSON.stringify(arguments)@UseCache() inherits the module TTL, which defaults to 5 seconds. A cached
value is reused unless it is null or undefined; values such as false, 0,
and '' are valid cache hits. Results that are null or undefined are not
written to the cache.
Configure the TTL
String TTLs use ms syntax. Numeric TTLs are always milliseconds, matching
cache-manager and Keyv. A decorator TTL overrides the module TTL for that
method:
export class CatalogService {
@UseCache({ ttl: '30 seconds' })
async findFeatured(): Promise<Product[]> {
return this.products.findFeatured();
}
@UseCache({ ttl: 250 })
async findFastChanging(): Promise<Product[]> {
return this.products.findFastChanging();
}
}Customize the key
getId receives the original method arguments and produces the suffix after
ClassName#methodName::
export class CatalogService {
@UseCache({ getId: (id: string) => id })
async findOne(id: string): Promise<Product> {
return this.products.findById(id);
}
}For this example, findOne('sku-1') uses the key
CatalogService#findOne:sku-1.
Bypass caching conditionally
When filter returns false, the decorator calls the original method without a
cache read or write:
export class CatalogService {
@UseCache({ filter: (_id: string, refresh: boolean) => !refresh })
async findOne(id: string, _refresh = false): Promise<Product> {
return this.products.findById(id);
}
}Cache backend errors and decorated method errors are propagated to the caller. Concurrent misses are not coalesced; each miss keeps the historical behavior of calling the decorated method independently.
Read, write, and invalidate directly
CacheService keeps the existing static API:
import { CacheService } from '@nest-mods/use-cache';
const key = 'CatalogService#findOne:sku-1';
await CacheService.cacheSet(key, product, '30 seconds');
const cached = await CacheService.cacheGet<Product>(key);
await CacheService.cacheDel(key);
await CacheService.cacheReset();Calling these methods before Nest initializes UseCacheModule is a no-op and
returns undefined. cacheReset() clears entries in the configured Redis
namespace.
Public API
The package root exports:
UseCacheModuleUseCacheModuleOptionsUseCacheModuleAsyncOptionsCacheServiceUseCacheUseCacheOptions
No internal subpaths are exported.
Development and build
Deno 2.9.3 manages dependency installation, tasks, formatting, linting, type
checking, and BDD tests. TypeScript 5.9.3 tsc is used only to compile the Node
package:
deno install --frozen
deno fmt --check
deno lint --quiet
deno task check
deno task build
deno task test:unit
deno task test:integrationThe integration suite expects Redis at redis://127.0.0.1:6379. REDIS_URL may
override the URL only with another loopback host; the repository does not start
Redis locally.
TypeScript 7 is not part of the initial build. The published package is pinned to the verified TypeScript 5.9 dual-output path until a later ESM-first or new dual-output design is validated.
Module formats
The package provides conditional exports for native ESM and CommonJS, with matching TypeScript declarations, source maps, and declaration maps for each branch. Each condition resolves directly to its independently compiled module graph.
License
MIT
