HTTP cache semantics library implementing RFC 7234/9111 rules for proper caching behavior
npx @tessl/cli install tessl/npm-http-cache-semantics@4.2.0HTTP Cache Semantics is a JavaScript library that implements HTTP caching rules according to RFC 7234/9111 specifications. It provides the CachePolicy class that determines when HTTP responses can be reused from cache, handles cache validation, and manages complex caching scenarios including Vary headers, proxy revalidation, and authenticated responses.
npm install http-cache-semanticsconst CachePolicy = require("http-cache-semantics");For ES modules:
import CachePolicy from "http-cache-semantics";const CachePolicy = require("http-cache-semantics");
// Create policy from request and response
const policy = new CachePolicy(
{
url: "/api/users",
method: "GET",
headers: { "accept": "application/json" }
},
{
status: 200,
headers: { "cache-control": "public, max-age=3600" }
}
);
// Check if response can be stored
if (policy.storable()) {
// Store in cache with TTL
cache.set(request.url, {
policy: policy,
body: response.body
}, policy.timeToLive());
}
// Later, check if cached response satisfies new request
const newRequest = { url: "/api/users", headers: {} };
if (policy.satisfiesWithoutRevalidation(newRequest)) {
// Use cached response with updated headers
return {
headers: policy.responseHeaders(),
body: cachedBody
};
}Creates a new cache policy instance that tracks cacheability and freshness of HTTP responses.
/**
* Creates a new CachePolicy instance
* @param {HttpRequest} req - Incoming client request
* @param {HttpResponse} res - Received server response
* @param {Object} [options={}] - Configuration options
* @param {boolean} [options.shared=true] - Is cache shared (public proxy) vs private
* @param {number} [options.cacheHeuristic=0.1] - Fallback heuristic fraction for cache duration
* @param {number} [options.immutableMinTimeToLive=86400000] - Minimum TTL for immutable responses in ms
* @param {boolean} [options.ignoreCargoCult=false] - Override nonsense cache headers
*/
constructor(req, res, options = {})Methods for determining if a response can be stored and used from cache.
/**
* Returns true if the response can be stored in cache
* @returns {boolean} false if response must not be cached
*/
storable()
/**
* Checks if cached response satisfies new request without revalidation
* @param {HttpRequest} req - New incoming HTTP request
* @returns {boolean} true if cached response can be used directly
*/
satisfiesWithoutRevalidation(req)Comprehensive method for determining cache usage with support for stale-while-revalidate.
/**
* Evaluates how cached entry can satisfy new request
* @param {HttpRequest} req - New incoming HTTP request
* @returns {EvaluationResult} Object with revalidation and response info
*/
evaluateRequest(req)Method for getting properly updated headers when serving cached responses.
/**
* Returns updated response headers for serving cached response
* Removes hop-by-hop headers and updates Age/Date
* @returns {Record<string, string>} Updated headers object for client response
*/
responseHeaders()Methods for determining cache entry age, freshness, and time-to-live.
/**
* Returns current age of cached response in seconds
* @returns {number} Age in seconds including resident time
*/
age()
/**
* Returns maximum age (freshness lifetime) in seconds
* @returns {number} Max-age value in seconds
*/
maxAge()
/**
* Returns remaining time cache entry may be useful in milliseconds
* @returns {number} Time-to-live in milliseconds
*/
timeToLive()
/**
* Returns true if response is past its expiration date
* @returns {boolean} true if stale (may still be usable in some cases)
*/
stale()
/**
* Returns Date header timestamp or response time if invalid
* @returns {number} Timestamp in milliseconds
*/
date()Methods for working with stale responses using RFC 5861 extensions.
/**
* Returns true if stale-while-revalidate is currently allowed
* @returns {boolean} true if stale response can be used while revalidating
*/
useStaleWhileRevalidate()Methods for efficiently updating stale cache entries by revalidating with origin server.
/**
* Returns headers for revalidation request to origin server
* Allows server to return 304 Not Modified to save bandwidth
* @param {HttpRequest} incomingReq - New incoming HTTP request
* @returns {Record<string, string>} Headers object for revalidation request
*/
revalidationHeaders(incomingReq)
/**
* Creates updated policy from revalidation response
* @param {HttpRequest} request - Latest HTTP request for cached entry
* @param {HttpResponse} response - Revalidation response from origin server
* @returns {RevalidationResult} Object with updated policy and modification status
*/
revalidatedPolicy(request, response)Methods for storing and restoring cache policy objects.
/**
* Serializes CachePolicy instance to JSON-serializable object
* @returns {Object} Serialized object for storage
*/
toObject()
/**
* Creates CachePolicy instance from serialized object
* @param {Object} obj - Previously serialized CachePolicy object
* @returns {CachePolicy} Restored CachePolicy instance
*/
static fromObject(obj)/**
* Returns current time in milliseconds (can be monkey-patched for testing)
* @returns {number} Current timestamp
*/
now()/**
* HTTP request object
* @typedef {Object} HttpRequest
* @property {Record<string, string>} headers - Request headers (lowercase names)
* @property {string} [method="GET"] - HTTP method
* @property {string} [url] - Request URL
*/
/**
* HTTP response object
* @typedef {Object} HttpResponse
* @property {Record<string, string>} headers - Response headers (lowercase names)
* @property {number} [status=200] - HTTP status code
*/
/**
* Result from evaluateRequest method
* @typedef {Object} EvaluationResult
* @property {Object} [response] - Cached response can be used
* @property {Record<string, string>} [response.headers] - Updated headers for cached response
* @property {Object} [revalidation] - Revalidation needed
* @property {Record<string, string>} [revalidation.headers] - Headers for revalidation request
* @property {boolean} [revalidation.synchronous] - Whether to wait for revalidation
*/
/**
* Result from revalidatedPolicy method
* @typedef {Object} RevalidationResult
* @property {CachePolicy} policy - Updated CachePolicy instance
* @property {boolean} modified - Whether response body changed
* @property {boolean} matches - Whether revalidation response matches
*/const CachePolicy = require("http-cache-semantics");
// Cache storage implementation
const cache = new Map();
async function handleRequest(incomingRequest) {
// Try to get from cache
const cached = cache.get(incomingRequest.url);
if (!cached) {
// Cache miss - fetch from origin
const originResponse = await fetch(incomingRequest);
const policy = new CachePolicy(incomingRequest, originResponse);
if (policy.storable()) {
cache.set(incomingRequest.url, {
policy: policy,
body: originResponse.body
});
}
return {
headers: policy.responseHeaders(),
body: originResponse.body
};
}
// Evaluate cached entry
const { revalidation, response } = cached.policy.evaluateRequest(incomingRequest);
if (revalidation) {
// Need to revalidate with origin
const revalidationRequest = {
...incomingRequest,
headers: revalidation.headers
};
const revalidationResponse = await fetch(revalidationRequest);
const { policy: newPolicy, modified } = cached.policy.revalidatedPolicy(
incomingRequest,
revalidationResponse
);
const body = modified ? revalidationResponse.body : cached.body;
if (newPolicy.storable()) {
cache.set(incomingRequest.url, {
policy: newPolicy,
body: body
});
}
if (revalidation.synchronous) {
// Must wait for revalidation
return {
headers: newPolicy.responseHeaders(),
body: body
};
}
}
// Use cached response
return {
headers: response.headers,
body: cached.body
};
}