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
CachePolicyVarynpm 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
};
}