Route-Cache
route-cache is an Express.js middleware designed for efficient server-side caching of HTTP route responses. Its primary goal is to accelerate backend performance for frequently accessed routes, alleviate server load during periods of high demand, and mitigate issues like the 'thundering herd' phenomenon by serving pre-computed content for a defined period (TTL). The package is currently at version 0.7.0. Release cadence appears sporadic, with the last notable update being 0.6.1, which included status code caching. Key advantages of `route-cache` include broad support for various content types, proper handling of HTTP redirects, the ability to implement conditional caching logic per request, including dynamic cache key generation based on `req` and `res` objects, and seamless integration with gzip compression. It provides both an in-memory caching solution (leveraging `lru-cache` internally) and extensibility for distributed caching mechanisms through pluggable stores, such as `IoRedisStore` for Redis integration. This flexibility makes it suitable for both small-scale applications and larger, distributed systems requiring shared cache states.
Common errors
-
TypeError: Cannot read properties of null (reading 'json')
cause An issue where JSON parsing failed if the response body was null, fixed in v0.4.7.fixUpgrade `route-cache` to version 0.4.7 or higher to resolve the underlying JSON null error. -
Old content is being served even after data has changed.
cause The route is being cached, and the cache has not expired or been explicitly cleared.fixEither decrease the `ttl` (Time-To-Live) for `cacheSeconds`, or explicitly call `routeCache.removeCache('/your-route')` after data updates to invalidate the specific cached entry. For production, consider using distributed caching with invalidation hooks. -
Responses for different users are getting mixed up in the cache.
cause The default cache key (`req.originalUrl`) does not differentiate between personalized content for different users.fixImplement a custom cache key function for the affected route. The key should incorporate identifying information like `req.session.userId` or a unique user identifier, e.g., `routeCache.cacheSeconds(ttl, (req, res) => req.originalUrl + '|' + res.locals.userId);`.
Warnings
- breaking Prior to v0.1.2, `route-cache` might cache HTTP responses with status codes >= 400 (e.g., error pages). From v0.1.2 onwards, it explicitly avoids caching these invalid responses, which changes caching behavior for applications relying on the previous, less desirable default.
- breaking Version v0.6.1 introduced status code caching. This means the HTTP status code itself will be part of the cached response. While this is generally an improvement, it could subtly alter behavior if previous versions were used where the status code was re-evaluated on subsequent requests to a cached body.
- gotcha By default, `route-cache` uses `req.originalUrl` as the cache key. For applications with dynamic content based on user sessions, query parameters (that are not part of `originalUrl` if not configured), or other request-specific data, this default key might lead to incorrect or shared cached responses.
- gotcha The package documentation primarily uses CommonJS `require` syntax. Directly using ES module `import` syntax might not work out-of-the-box in pure ESM environments without proper configuration (e.g., transpilation or specific Node.js loader setup) or if the package does not explicitly provide ESM exports.
Install
-
npm install route-cache -
yarn add route-cache -
pnpm add route-cache
Imports
- routeCache
import routeCache from 'route-cache';
const routeCache = require('route-cache'); - cacheSeconds
import { cacheSeconds } from 'route-cache';app.get('/path', routeCache.cacheSeconds(ttl), ...); - IoRedisStore
import IoRedisStore from 'route-cache/ioRedisStore';
const IoRedisStore = require('route-cache/ioRedisStore');
Quickstart
const express = require('express');
const routeCache = require('route-cache');
const app = express();
// Cache a route for 20 seconds using the default cache key (req.originalUrl)
app.get('/cached-index', routeCache.cacheSeconds(20), (req, res) => {
console.log('This message will only appear once every 20 seconds for /cached-index');
res.send('This response is cached!');
});
// Cache a route with a custom, dynamic key based on user authentication status
app.get('/user-data/:id', routeCache.cacheSeconds(60, (req, res) => {
// Assuming res.locals.isAuthenticated indicates user login status
if (!res.locals.isAuthenticated) return false; // Do not cache for unauthenticated requests
return req.originalUrl + '|' + req.params.id + '|' + res.locals.userId;
}), (req, res) => {
res.locals.isAuthenticated = Math.random() > 0.5; // Simulate auth status
res.locals.userId = res.locals.isAuthenticated ? 'user123' : 'guest';
console.log(`Serving user data for ${req.params.id}. Authenticated: ${res.locals.isAuthenticated}`);
res.send(`Data for user ${req.params.id}. (Auth: ${res.locals.isAuthenticated})`);
});
// Example of how to explicitly remove a cached route
app.post('/clear-cache', (req, res) => {
const routeToClear = req.body.route || '/cached-index';
routeCache.removeCache(routeToClear);
res.send(`Cache for ${routeToClear} cleared.`);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
console.log('Try visiting /cached-index multiple times, and /user-data/1 and /user-data/2');
});