tools.cache and (when using BullMQ) tools.queue via the same REDIS_URL.
Why
Redis gives you sub-millisecond cache reads with a minimal operational footprint. In local dev, the samememory Redis container used by BullMQ doubles as the cache store, so no extra infrastructure is needed. In production, point REDIS_URL at a managed Redis instance (e.g. Railway Redis, Upstash, or ElastiCache).
Setup
- In
development.ts,production.ts, andtest.ts, settools.cache: - In
compose.yml, thememoryservice provides Redis for both cache and queue in dev: - In
.env.development, set: - Run
make test module=backend.
Built-in helpers
The cache client ships three pre-built helpers, all accessed throughtools.cache.helpers:
| Helper | Key prefix | Purpose |
|---|---|---|
webhooksEvents | webhooks_events | Idempotency check for processed Stripe webhook events |
invoicePreview | organization:invoice_preview | Caches invoice preview results per organisation |
globalConfiguration | global_configuration | Caches global app configuration |
Adding a new cache helper
- Create
apps/backend/src/tools/cache/<domain>.ts. Export abuild<Domain>CacheHelper(keyPrefix, logger, redisClient)factory that returns typed get/set/del methods. - Register the helper in
buildCacheClientinsideapps/backend/src/tools/cache/index.ts: - Run
make test module=backend.
Gotchas
REDIS_URLis shared betweentools.cacheandtools.queue(BullMQ). Changing one affects the other — keep them consistent or split intentionally.- Never paste a production
REDIS_URLinto.env.development— rotate credentials immediately if that happens. - Redis key collisions are possible if two helpers use overlapping prefixes. Always use distinct, namespaced prefixes.
- The
redisClient.connect()call is awaited at startup. A misconfigured or unreachable Redis URL will prevent the server from starting.
What’s next?
- BullMQ — the queue tool that shares the same Redis instance.
- Configuration — environment-level config.