Express middleware providing "on demand" caching that kicks in only when requests arrive simultaneously.
npm install express-cache-on-demand
var expressCacheOnDemand = require('express-cache-on-demand')();
// Fetch a page from a database and build fancy
// navigation, then render the template; this is
// just an example of a big, possibly slow task.
// Use the expressCacheOnDemand middleware to
// send the same response to all simultaneous
// requests.
app.get('/:page', expressCacheOnDemand, function(req, res) {
return getPage(req.params.page, function(page) {
return addFancyNavigation(page, function(links) {
return res.render('page.html', { links: links });
});
});
});
Under light load, with requests arriving far apart, every request for a given req.url
will get an individually generated response, which gives them the newest content. This is the same behavior you see without the middleware.
But under heavy load, with new requests arriving while the first request is still being processed, the additional requests are queued up. When the first response is ready, it is simply sent to all of them. And then the response is discarded, so that the next request to arrive will generate a new response with the latest content.
This gives us "on demand" caching. The server is still allowed to generate new responses often, just not many of them simultaneously. It is the shortest practical lifetime for cached data and largely eliminates concerns about users seeing old content, as well as concerns about cache memory management.
This middleware is intended for routes that potentially take a long time to generate a relatively small response (under a megabyte, let's say). Dynamic web pages with lots of complicated moving parts are a perfect example.
You should not use this middleware for your entire site. In particular:
- It does not work and is not suitable anyway for routes that deliver entire files via
res.sendFile
and related methods - It does not work and is not suitable anyway for routes that
pipe
content intores
- It shouldn't be registered globally before the
express.static
middleware
There may be other possible endings for an Express res
object that are not properly handled by this middleware yet. Pull requests welcome.
By default, the middleware only caches requests when:
req.method
isGET
orHEAD
.req.user
is falsy.req.session
is empty (*).
If the above conditions are not met, every request will generate its own response. This way we don't cause surprising behavior for logged-in users who are modifying site content and seeing personalized displays.
(*) The middleware is smart enough to ignore a few special cases, such as req.session.cookie
, an empty req.session.flash
, and an empty req.session.passport
.
If you don't like our rules for caching, you can write your own. Just pass a function that returns false
for requests that should not be hashed, and a hash key such as req.url
for requests that should be hashed.
var expressCacheOnDemand =
require('express-cache-on-demand')(hasher);
function hasher(req) {
if (req.url.match(/nevercacheme/)) {
return false;
}
return req.url;
}
This module is an Express middleware wrapper for our cache-on-demand module. If you would like to do the same trick with code that isn't powered by Express, try using that module directly.
express-cache-on-demand
was created at P'unk Avenue for use in many projects built with Apostrophe, an open-source content management system built on node.js. If you like cache-on-demand
you should definitely check out apostrophecms.org.
Feel free to open issues on github.
- The default hash function now correctly refuses to cache in the documented circumstances (i.e. logged-in users or a nontrivial
req.session
object). Previously areturn false
was missing, resulting in the possibility of a cached result going to a user with a different session. var
has been eliminated and the code has been lightly refactored without other changes to behavior.
res.getHeader
support. Thanks to Vadim Fedorov.
cache-on-demand
is now at 1.0.0 also, plus the lodash dependency now points to a modern release. No functional changes.
redirect
now supports the optional status code argument properly. Thanks to Alexey Astafiev.
This module has been in successful production use for many moons, so we're declaring it stable (1.0.0).
Fixed a bug in redirect
support, which now works properly.
Initial release. With shiny unit tests, of course.