Stack
- @Cache on the root @Module — in-memory or Redis; one plugin may declare multiple named caches
- @CacheClient(plugin, name?) — inject an ICache from this plugin or another
- @CacheRoute / @DeleteCacheRoute — HTTP response cache keyed by plugin + name
- @CacheFn / @DeleteCacheFn — method result cache keyed by plugin + name
- Cross-plugin reuse — consumer plugins inject the owner’s cache; they do not re-declare @Cache for the same client
@Cache on the root module
@Cache must sit on the @Module class — not on services or controllers. Pass a unique name when the plugin opens more than one cache. Multiple @Cache decorators on the same module class are allowed.
- type — "in-memory" or Redis options (host/port/password from env)
- name — optional client id (required when one plugin declares multiple @Cache entries)
- checkperiod / ttl / other options — follow the shared-backend-core Cache options for that type
- Owner is the loading plugin — consumers address it by that plugin’s metadata.name (or CacheManager.DEFAULT_PLUGIN for core)
1import { Cache, Module } from "@quan-erp/shared-backend-core";
2import metadata from "../../module.metadata.json" with { type: "json" };
3
4@Cache({
5 name: "publisher",
6 type: "in-memory",
7 checkperiod: 1000,
8})
9@Cache({
10 name: "session",
11 type: "redis",
12 host: process.env.REDIS_HOST,
13 port: Number(process.env.REDIS_PORT ?? 6379),
14 password: process.env.REDIS_PASSWORD,
15})
16@Module({
17 name: metadata.name,
18 providers: [...],
19 controllers: [...],
20})
21export class MyPluginModule {}@CacheClient
Inject an ICache on a service property. The first argument is the plugin that owns the cache; the second is the optional connection name when that plugin declared more than one @Cache.
- CacheManager.DEFAULT_PLUGIN — core / shared default cache owner
- metadata.name — this plugin’s own cache (add name when it has several)
- "other-plugin" — a cache created by another plugin (same process / registry)
- Second arg name — named client (e.g. "publisher", "session")
1import {
2 CacheClient,
3 CacheManager,
4 Service,
5} from "@quan-erp/shared-backend-core";
6import type { ICache } from "@quan-erp/shared-backend-core";
7import metadata from "../../module.metadata.json" with { type: "json" };
8
9@Service()
10export class PublisherService {
11 // Core / default owner
12 @CacheClient(CacheManager.DEFAULT_PLUGIN, "publisher")
13 private defaultPublisher: ICache;
14
15 // This plugin’s named cache
16 @CacheClient(metadata.name, "session")
17 private session: ICache;
18
19 // Cache owned by another plugin
20 @CacheClient("warehouse", "replica")
21 private warehouseReplica: ICache;
22}Use another plugin’s cache
Caches live in a process-wide registry keyed by owner plugin (+ optional name). A consumer does not re-declare @Cache for that client — it injects the owner’s cache and points route/method helpers at the same plugin + name.
- Owner plugin — declares @Cache({ name, … }) and boots first (list it in pluginDependencies)
- Consumer plugin — @CacheClient("owner-plugin", "cache-name")
- Route / method helpers — set plugin: "owner-plugin" and name: "cache-name"
- Do not hard-code a second identical @Cache in every consumer — reuse the owner’s client
- Ensure the owner plugin is installed and loaded before consumers that inject its ICache
1import { CacheClient, Service } from "@quan-erp/shared-backend-core";
2import type { ICache } from "@quan-erp/shared-backend-core";
3
4@Service()
5export class ReportService {
6 // "warehouse" plugin declared @Cache({ name: "replica", ... })
7 @CacheClient("warehouse", "replica")
8 private warehouseReplica: ICache;
9}Route-level
- @CacheRoute({ key, plugin, name }) — key may be string or (req) => string
- @DeleteCacheRoute({ key, plugin, name }) — callback key must return string[]
- plugin + name must match the owning @Cache (including another plugin’s client)
1import {
2 CacheRoute,
3 Controller,
4 DeleteCacheRoute,
5 Get,
6 Post,
7} from "@quan-erp/shared-backend-core";
8
9@Controller("/branch")
10export class BranchController {
11 @Get("/")
12 @CacheRoute({
13 key: (req) => `branch-${req.query.skip}-${req.query.limit}`,
14 plugin: "warehouse",
15 name: "replica",
16 })
17 async list() { /* … */ }
18
19 @Post("/")
20 @DeleteCacheRoute({
21 key: (req) => [`branch-${(req as any).user.payload.id}`],
22 plugin: "warehouse",
23 name: "replica",
24 })
25 async create() { /* … */ }
26}Method-level
- @CacheFn({ key, plugin, name }) — key is (...args) => string
- @DeleteCacheFn({ key, plugin, name }) — key returns string[] (wildcards allowed)
- Same plugin + name rules as @CacheClient / route helpers
1import {
2 CacheFn,
3 CacheManager,
4 DeleteCacheFn,
5 Service,
6} from "@quan-erp/shared-backend-core";
7import type { CreateJobDTO } from "./create-job.dto.js";
8
9@Service()
10export class JobService {
11 @CacheFn({
12 plugin: CacheManager.DEFAULT_PLUGIN,
13 name: "publisher",
14 key: (skip: number, limit: number) => `active-jobs-${skip}-${limit}`,
15 })
16 async getActiveJobs(skip: number, limit: number) { /* … */ }
17
18 @DeleteCacheFn({
19 plugin: CacheManager.DEFAULT_PLUGIN,
20 name: "publisher",
21 key: () => [`active-jobs-*`],
22 })
23 async register(dto: CreateJobDTO) { /* … */ }
24}