A cache dependency module for Nest framework (node.js) https://nestjs.com/
nest-cache-dependency consider dependencies. Delete those that depend on the deletion.
TODO: Write more documentation.
$ npm i --save @anchan828/nest-cache-dependency
- Import module
@Module({
imports: [CacheDependencyModule.register()],
})
export class AppModule {}
@Controller()
@UseInterceptors(CacheDependencyInterceptor)
export class ExampleController {
constructor(private readonly service: ExampleService) {}
@Get("users/:userId/items")
@CacheKey("users/:userId/items")
@CacheDependency<Item[]>((cacheKey: string, items: Item[], graph: CacheDependencyGraph) => {
for (const item of items) {
// e.g. users/1/items/2
graph.addNode(`${cacheKey}/${item.id}`, item);
graph.addDependency(`${cacheKey}/${item.id}`, cacheKey);
}
})
public getItems(): Item[] {
return this.service.getItems();
}
@Get("users/:userId/items/:itemId")
@CacheKey("users/:userId/items/:itemId")
public getItem(@Param("userId", ParseIntPipe) userId: number, @Param("itemId", ParseIntPipe) itemId: number): Item {
return this.service.getItem(userId, itemId);
}
@Delete("users/:userId/items/:itemId")
@ClearCacheDependencies("users/:userId/items/:itemId")
public deleteItem(
@Param("userId", ParseIntPipe) userId: number,
@Param("itemId", ParseIntPipe) itemId: number,
): void {
this.service.deleteItem(userId, itemId);
}
}
@Injectable()
export class ExampleService {
constructor(private readonly cacheService: CacheDependencyService) {}
private items: Item[] = Array(5)
.fill(0)
.map((_, index) => ({ id: index, name: `Item ${index}` }));
public async getItems(userId: number): Promise<Item[]> {
const cacheKey = `users/${userId}/items`;
const cache = await this.cacheService.getCache<Item[]>(cacheKey);
if (cache) {
return cache;
}
await this.cacheService.createCacheDependencies((graph: CacheDependencyGraph) => {
graph.addNode(cacheKey, this.items);
for (const item of this.items) {
graph.addNode(`${cacheKey}/${item.id}`, item);
graph.addDependency(`${cacheKey}/${item.id}`, cacheKey);
}
});
return this.items;
}
public async getItem(userId: number, itemId: number): Promise<Item> {
const cacheKey = `users/${userId}/items/${itemId}`;
const cache = await this.cacheService.getCache<Item>(cacheKey);
if (cache) {
return cache;
}
const item = this.items.find((item) => item.id === itemId);
await this.cacheService.setCache(cacheKey, item);
return item;
}
public async deleteItem(userId: number, itemId: number): Promise<void> {
this.items = this.items.filter((item) => item.id !== itemId);
await this.cacheService.clearCacheDependencies(`users/${userId}/items/${itemId}`);
}
}
Sometimes you don't want to use the old cache when you update your application.
You can set prefix string to key as version.
@Module({
imports: [
CacheDependencyModule.register({
cacheDependencyVersion: "v1",
}),
],
})
export class AppModule {}
If you set v1
and call cacheDependencyService.set("users/1", user)
, cache manager will save value to v1:users/1
key, not users/1
You can use Pub/Sub pattern of Redis. This works well when it is a combination of in-memory cache and multiple instances. A message will be sent to all instances when the cache is explicitly deleted.
@Module({
imports: [
CacheDependencyModule.register({
pubsub: { host: "localhost" },
}),
],
})
export class AppModule {}
@CacheKey
supports route-parser. You can use :name
pattern as cache key
CacheDependencyGraph
uses dependency-graph