Skip to content

Caching

Nori provides a pluggable caching layer with TTL (time-to-live) support. Use it to cache expensive queries, computed values, or entire HTTP responses.


Configuration (.env)

Var Values Default
CACHE_BACKEND memory, redis memory
CACHE_MAX_KEYS Integer (for memory backend) 10000
REDIS_URL Redis connection string redis://localhost:6379

The memory backend stores values in-process using a Least Recently Used (LRU) eviction strategy. When the CACHE_MAX_KEYS limit is reached, the oldest/least used entries are automatically removed to prevent memory exhaustion.


Convenience Functions

from core.cache import cache_get, cache_set, cache_delete, cache_flush

# Store a value with a 5-minute TTL (default: 300 seconds)
await cache_set('user:42:profile', user_data, ttl=300)

# Retrieve — returns None if expired or missing
profile = await cache_get('user:42:profile')

# Delete a specific key
await cache_delete('user:42:profile')

# Flush the entire cache
await cache_flush()

Response Caching Decorator

The @cache_response decorator caches the full HTTP response for GET requests. Non-GET requests pass through uncached. Only successful responses (2xx) are cached — error responses (4xx, 5xx) are never stored.

from core.cache import cache_response

class ReportController:

    @cache_response(ttl=60)  # Cache for 60 seconds
    async def dashboard(self, request):
        # Expensive query — only runs once per TTL window
        stats = await compute_dashboard_stats()
        return JSONResponse(stats)

Cache keys are generated automatically from the request path and query string. You can customize the prefix:

@cache_response(ttl=120, key_prefix='reports')
async def monthly(self, request):
    ...

Backends

MemoryCacheBackend (default)

In-process LRU cache with TTL enforcement on read. Zero configuration, ideal for development and single-process deployments.

  • LRU eviction: When the store reaches max_keys (default: 10,000), the least-recently-used entry is evicted on insert. Reads and updates refresh an entry's position.
  • Configurable limit: Set CACHE_MAX_KEYS in .env to override the default (e.g., CACHE_MAX_KEYS=50000).
  • TTL expiry: Expired entries are evicted lazily on read.

Production note: Each Gunicorn worker maintains its own isolated cache, so state is not shared across workers. Use redis in production for shared cache and rate limiting.

RedisCacheBackend

Requires CACHE_BACKEND=redis and a valid REDIS_URL. Uses Redis SETEX for atomic TTL. Shared across all workers. The Redis serializer handles common Python types (datetime, date, UUID, Decimal) automatically; other non-JSON-serializable types will raise TypeError instead of being silently converted.

Direct Backend Access

For advanced use cases, you can access the backend instance directly:

from core.cache import get_backend

backend = get_backend()
await backend.set('key', 'value', ttl=60)