Goal & when
Use caching for repeated reads, session/token stores, and expensive computations. Declare the client once on the owning plugin’s root module, then inject it anywhere in the same process — including other plugins.
- In-memory — single-process, simple TTL / checkperiod
- Redis — shared across instances; host / port / password from env
- Cross-plugin — inject the owner’s cache; do not re-declare @Cache for the same client
1. Prerequisites
You need a root @Module and, for Redis, reachable credentials from PluginEnv / base .env.
- Root @Module({ name: metadata.name, … }) exists
- Redis — REDIS_HOST / REDIS_PORT / REDIS_PASSWORD (or your chosen env names) available at boot
- If consuming another plugin’s cache, list that owner in pluginDependencies
- @Cache must sit on the @Module class — not on services or controllers
2. Declare @Cache on the root module
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, …)
- name — optional client id; required when one plugin declares multiple @Cache entries
- Owner is the loading plugin — consumers address it by that plugin’s metadata.name
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 {}3. Inject with @CacheClient
Inject an ICache on a service property. The first argument is the plugin that owns the cache; the second is the optional client name.
- metadata.name — this plugin’s own cache (add name when it has several)
- "other-plugin" — a cache created by another plugin
- CacheManager.DEFAULT_PLUGIN — core / shared default cache owner when applicable
1import {
2 CacheClient,
3 Service,
4} from "@quan-erp/shared-backend-core";
5import type { ICache } from "@quan-erp/shared-backend-core";
6import metadata from "../../module.metadata.json" with { type: "json" };
7
8@Service()
9export class SessionService {
10 @CacheClient(metadata.name, "session")
11 private session: ICache;
12
13 async getToken(userId: string) {
14 return this.session.get(`token:${userId}`);
15 }
16}4. Route & method helpers
Point helpers at the same plugin + name as the owning @Cache. Use delete helpers when writes invalidate cached reads.
- @CacheRoute / @DeleteCacheRoute — HTTP response cache; key may be string or (req) => string
- @CacheFn / @DeleteCacheFn — method result cache; key is (...args) => string
- plugin + name must match the owning @Cache (including another plugin’s client)
1@CacheRoute({
2 key: (req) => `orders:${req.query.status ?? "all"}`,
3 plugin: metadata.name,
4 name: "session",
5})
6@Get("/orders")
7list() { /* … */ }1@CacheFn({
2 key: (id: number) => `order:${id}`,
3 plugin: metadata.name,
4 name: "session",
5})
6async findOne(id: number) { /* … */ }5. Cross-plugin reuse
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 helpers at the same plugin + name.
- Owner — declares @Cache({ name, … }) and boots first (pluginDependencies)
- Consumer — @CacheClient("owner-plugin", "cache-name")
- Route / method helpers — same owner plugin + name
- Do not hard-code a second identical @Cache in every consumer
1@Service()
2export class ReportService {
3 // "warehouse" plugin declared @Cache({ name: "replica", ... })
4 @CacheClient("warehouse", "replica")
5 private replica: ICache;
6}6. Verify
- Plugin boots without cache / Redis connection errors
- Injected ICache get / set / del behaves as expected
- Cached routes return the same payload until invalidated
- @DeleteCacheRoute / @DeleteCacheFn clears the intended keys
Common mistakes
- Putting @Cache on a service or controller instead of the root @Module
- Omitting name when the plugin declares more than one @Cache
- Hard-coding Redis credentials instead of env
- Re-declaring @Cache in consumers instead of @CacheClient
- Mismatching plugin + name between @CacheClient and @CacheRoute / @CacheFn
- Forgetting pluginDependencies so the owner loads after the consumer