Cache Patterns
Cache is for temporary, derived, or performance-oriented data.
It is not durable business storage.
When To Use Cache
| Question | Guidance |
|---|---|
| Use this when | Data can be recomputed, reloaded, or safely treated as temporary. |
| Avoid this when | The value is the only source of business truth or must survive driver loss. |
| Start with | Memory cache for one-process local development and focused tests. |
| Upgrade to | Shared cache when API, workers, scheduler, or multiple hosts need the same values, locks, or counters. |
Generated Accessors
Generated Apps expose cache through default and named accessors:
app.Cache()
app.Caches().Sessions()Named cache scopes come from environment variables:
CACHE_SUPPORTED_DRIVERS=memory,redis
CACHE_DRIVER=memory
CACHE_SESSIONS_DRIVER=redisAfter adding or renaming named caches, use the normal build path:
forj buildDev Loop
When this App is listed in dev.apps, its build lifecycle normally runs forj build for you.
Use focused generation only when you intentionally want to refresh cache code without a full build:
forj generate --cacheCache Shell
Redis-backed cache stores can be inspected with the generated cache:shell command:
forj cache
forj cache:shellPass a cache store name when the App has named Redis caches:
forj cache sessions
forj cache --store sessionsGoForj tries redis-cli first, then falls back to the generated Docker Compose redis service when one exists:
forj cache --method local
forj cache --method compose
forj cache --printRun one Redis command non-interactively, or pass native redis-cli arguments after --:
forj cache --exec "PING"
forj cache sessions --exec "GET user:1"
forj cache -- PING
forj cache sessions -- GET user:1Good Uses
Cache is a good fit for:
- expensive derived data
- session-like temporary state when the selected driver supports the requirement
- rate limit counters
- short-lived lookup results
- coordination locks when the operational tradeoff is understood
Set TTLs deliberately.
Choosing Cache Drivers
Use this default path:
| Need | Driver Shape |
|---|---|
| Fast local development or unit tests | memory |
| Local persistence across restarts | file |
| Shared cache across API, workers, or scheduler | Redis, Memcached, NATS, DynamoDB, or SQL-backed cache |
| Distributed locks or rate limits | shared backend with explicit TTLs |
Use memory cache until process boundaries make that wrong. A memory cache is not shared between api, worker, and scheduler processes.
Cache-Aside Shape
Typical flow:
- try cache
- compute or load source-of-truth data
- write cache with TTL
- return result
Cache misses should be normal.
Local and Production Drivers
Use memory or file cache locally.
Use Redis, Memcached, NATS, DynamoDB, or SQL-backed cache when production requirements need shared, durable, or distributed behavior.
Use Cache for the full package-level driver matrix.
Common Mistakes
Common mistakes
- Do not store source-of-truth business state only in cache.
- Do not omit TTLs for data that should expire.
- Do not put user input directly into metric labels or cache resource names.
- Do not import cache driver packages into business services.
- Do not assume local memory cache is shared across runtime processes.
Next Steps
- Named Resources explains named accessors.
- Driver Selection explains backend choices.
- Environment Reference lists cache and driver settings.
- Cache covers standalone package behavior.
