Response Cache
The response cache middleware stores GET responses in memory and serves them directly on subsequent requests, bypassing the downstream handler. It adds X-Cache: HIT or X-Cache: MISS headers so you can verify caching behavior. Use it for endpoints that are expensive to compute but do not change frequently.
Basic setup
Section titled “Basic setup”Add cacheMiddleware early in your pipeline so cached responses skip as much work as possible:
const App = pidgn.Router.define(.{ .middleware = &.{ pidgn.errorHandler(.{}), pidgn.logger, pidgn.cacheMiddleware(.{ .cacheable_prefixes = &.{"/api/"}, .default_ttl_s = 300, }), pidgn.gzipCompress(.{}), pidgn.bodyParser, }, .routes = &.{ ... },});CacheConfig
Section titled “CacheConfig”| Option | Type | Default | Description |
|---|---|---|---|
cacheable_prefixes | []const []const u8 | &.{"/"} | URL path prefixes eligible for caching. A request is cached only if its path starts with one of these prefixes. |
default_ttl_s | u32 | 300 | Time-to-live in seconds for cached entries. After this period, the entry expires and the next request triggers a fresh response. |
How it works
Section titled “How it works”When a request arrives:
- GET only — non-GET requests pass through immediately.
- Prefix match — the request path is checked against
cacheable_prefixes. If no prefix matches, the request passes through. Cache-Control: no-cache— if the client sends this header, the cache is bypassed and the response is generated fresh.- Cache lookup — if a cached entry exists and has not expired, the cached body and content type are returned with an
X-Cache: HITheader. - Cache miss — the downstream handler runs. If it returns a
200 OKresponse with a body of 4096 bytes or less, the response is cached. AnX-Cache: MISSheader is added.
Responses larger than 4096 bytes are not cached to keep memory usage bounded.
Scoped caching
Section titled “Scoped caching”You can also apply the cache middleware to specific route groups using scoped routes:
.routes = &.{ pidgn.Router.get("/", index),
pidgn.Router.scope("/api/cached", .{ .middleware = &.{ pidgn.cacheMiddleware(.{ .cacheable_prefixes = &.{"/api/cached/"}, .default_ttl_s = 10, }), }, }, &.{ pidgn.Router.get("/time", cachedTimeHandler), pidgn.Router.get("/stats", cachedStatsHandler), }),},Cache invalidation
Section titled “Cache invalidation”Access the global cache instance to manually remove entries:
const cache = pidgn.getResponseCache();
// Invalidate a specific pathcache.remove("/api/cached/time");
// Or clear the entire cachecache.clear();This is useful when a write operation (POST, PUT, DELETE) should invalidate related cached GET responses.
Verifying cache behavior
Section titled “Verifying cache behavior”Check the X-Cache response header:
# First request -- cache misscurl -i http://127.0.0.1:4000/api/cached/time# X-Cache: MISS
# Second request -- cache hitcurl -i http://127.0.0.1:4000/api/cached/time# X-Cache: HIT
# Bypass cachecurl -i -H "Cache-Control: no-cache" http://127.0.0.1:4000/api/cached/time# X-Cache: MISSLimitations
Section titled “Limitations”- 4KB max body — responses larger than 4096 bytes are not cached. This keeps the in-memory footprint predictable.
- In-memory only — the cache lives in process memory and is lost on restart. There is no distributed or disk-based backend.
- GET only — only GET requests are cached. POST, PUT, DELETE, and other methods always pass through.
- No vary support — the cache key is the request path only. It does not account for query parameters, headers, or content negotiation.
Next steps
Section titled “Next steps”- Middleware — understand middleware ordering and scoping
- Static Files — serve files from disk with ETag caching
- Asset Pipeline — fingerprinted assets for long-lived browser caching
- Context — response helpers and the request/response lifecycle
- Performance — other strategies for improving response times