An express middleware that simplifies caching responses for HTTP requests of any
type. It also handles many unique cases such as piping data, using sessions, and
ETags. This middleware is implemented at the socket level, so it is entirely
transparent to the response methods such as res.json, res.end, etc. and
stays out of your way as a result. TypeScript support is also included.
Using the default settings it works by:
res.json, res.sendFile,
res.pipe, etc.express-session (req.session) aware. This ensures user data is not
accidentally shared when two users request the same URL.expeditious instancetimestring format for setting cache timeouts. For example you
can pass '1 hour' instead of 60 * 60 * 1000.x-expeditious-cache header that states either hit or
miss so clients can determine if the data is a cached copy.npm install express-expeditious --save
You can also install one of these to customise the cache storage location:
If you'd like to write an engine of your own for another storage system then take a look at the source code for those modules - it's pretty easy and there's more information here.
These examples will cache any successful request - this is a request that you send a 200 status code to the client.
const getExpeditiousCache = require('express-expeditious');
const express = require('express');
const cache = getExpeditiousCache({
// Namespace used to prevent cache conflicts, must be alphanumeric
namespace: 'expresscache',
// Store cache entries for 1 minute (can also pass milliseconds e.g 60000)
defaultTtl: '1 minute'
});
const app = express();
// the initial call to this will take 2 seconds, but any subsequent calls
// will receive a response instantly from cache for the next hour
app.get('/ping', cache.withTtl('1 hour'), (req, res) => {
setTimeout(() => {
res.end('pong');
}, 2000);
});
// Cache everything below this line for 1 minute (defaultTtl)
app.use(cache);
Similar to the regular JavaScript example:
import * as expeditious from 'express-expeditious';
import * as express from 'express';
const cacheoptions: expeditious.ExpeditiousOptions = {
namespace: 'expresscache',
defaultTtl: '1 minute'
};
const cache = expeditious(cacheoptions);
const app = express();
// the initial call to this will take 2 seconds, but any subsequent calls
// will receive a response instantly from cache for the next hour
app.get('/ping', cache.withTtl('1 hour'), (req, res) => {
setTimeout(() => {
res.end('pong');
}, 2000);
});
There's also a TypeScript sample folder in this repo at /example-ts.
Just like before, except we pass a redis engine:
const getExpeditiousCache = require('express-expeditious');
const express = require('express');
const cache = getExpeditiousCache({
namespace: 'expresscache',
defaultTtl: '1 minute',
engine: require('expeditious-engine-redis')({
// options for the redis driver
host: 'redis.acme.com',
port: 6379
})
});
const app = express();
app.use(cache);
app.get('/ping', (req, res) => {
setTimeout(() => {
res.end('pong');
}, 2000);
});
Currently cached data is stored in the following format:
{
headers: String
data: {
type: 'Buffer',
data: Uint8Array
}
}
For example, here's a cache entry that would be stored by the module:
{
"headers": "HTTP/1.1 200 OK\r\nX-Powered-By: Express\r\nx-expeditious-cache: hit\r\nDate: Thu, 14 Jun 2018 16:16:33 GMT\r\nConnection: close\r\nTransfer-Encoding: chunked",
"data": {
"type": "Buffer",
"data": [
13,
10,
13,
10,
49,
13,
10,
111,
13,
10,
49,
13,
10,
107,
13,
10,
48,
13,
10,
13,
10
]
}
}
This format could be changed to improve the ability to query for specific headers or data. The use of Buffers is to ensure binary/compressed data can be cached, but PRs and ideas surrounding this are welcome. For example, we could store non-binary data as strings to simplify the ability to query it.
You can modify cache behaviour for specific endpoint or routers easily like so:
const app = require('express')();
const isEmpty = require('lodash.isempty');
const products = require('lib/products');
const cache = require('express-expeditious')({
namespace: 'expresscache',
defaultTtl: '1 minute'
});
/**
* Exposes a /products endpoint that can be queried for a list of products.
*
* If the client provides a querystring then the request will NOT be cached.
*
* The endpoint is also session independent meaning the data returned is the
* same regardless of the user that calls, so we set sessionAware to false since
* it will reduce resource usage.
*/
app.get(
'/products',
cache
.withSessionAwareness(false)
.withCondition((req) => { return isEmpty(req.query) }),
(req, res, next) => {
products.list(req.query)
.then((listOfProducts) => res.json(listOfProducts))
.catch(next)
}
);
/**
* Expose a /orders endpoint that caches all HTTP 200 responses for 5 minutes.
* It overrides the default behaviour and also caches 404 responses for a given
* request for 1 minute - by default only 200 responses are cached
*/
app.use(
'/orders',
cache
.withTtl('5 minutes')
.withTtlForStatus('1 minute', 404),
require('lib/routes/orders')
);
If you need to enable logging for this module, simply run your application in a session with a DEBUG environment variable set to "express-expeditious" like so:
export DEBUG=express-expeditious
$ node your-app.js
This will have express-expeditious to enable the debug logger module it uses.
Here's the performance increase seen in simple benchmarks using Apache Bench.
All of these tests use expeditious-engine-memory for storage meaning the
node.js process memory is used.
You can run the same tests using the following commands:
# after cloning the repo
cd express-expeditious/
npm install
npm run benchmark-server
You need to run MongoDB locally on its default port of 27017 also. These tests were performed with MongoDB running inside docker using the following command:
docker run --name mongodb -p 27017:27017 -p 28017:28017 -d mongo
Now, to start the tests run the command below in another terminal:
ab -n 1000 -c 100 http://localhost:8080/$ENDPOINT_TO_TEST
Here are the requests per second averaged from 4 runs:

And here is the average time taken for each request averaged over 4 runs:

It's clear that with caching applied using express-expeditious latency is reduced so your application will feel more responsive and the amount of requests per second that can be served increases significantly.
See the example folder here.
You can hit HTTP endpoints on the example server using the following URLs:
res.render to render a homepageres.endres.sendFileres.piperes.writeAll of these URLs respond after 2 seconds on the first call, but subsequent calls will use express-expeditious to respond instantly using the cache.
This module is a factory function (similar to express) that returns a middleware function. A number of options are supported and are explained in the following sections.
Create a middleware instance using opts. Supported options are:
genCacheKey is also supplied
then this option is ignored.
from the cache. By default req.originalUrl is used as the key.x-expeditious-cache as the header key. Set a new string value to
customize the header or false if you prefer not to write the expeditious response header.expeditious option is not passed. Represents time entries
will remain in the cache. Can be set to any value the timestring module accepts.expeditious option is not passed. It's used as a namespace
to prevent cache conflicts.expeditious option is not passed. It is the storage engine for
caching.These options are covered in greater detail in the behaviours section below.
Returns a new cache middleware that has a defaultTtl setting of number, but
inherits other values of the parent instance.
const cache = require('express-expeditious')({
namespace: 'mycache',
defaultTtl: '30 minutes'
});
const userRoutes = require('./routes/users');
// Set cache time (defaultTtl) for /users to 15 minutes
app.use('/users', cache.withTtl('15 minutes'), userRoutes);
Creates a new cache instance with the given namespace. All other settings are inherited from the parent instance.
Creates a new cache instance with the given ttl for a specific status code.
All other settings are inherited from the parent instance. Previously supplied
values for statusCodeExpires will be used, but the values you pass to this
function will override if a conflict in values is found.
Returns a new cache middleware that has a shouldCache setting of its parent
overwritten by the passed function. Passing nothing will create an instance
without a shouldCache entry. Other settings are inherited.
Returns a new cache middleware that has a genCacheKey setting of its parent
overwritten by the passed function. Passing nothing will create an instance
without a genCacheKey entry. Other settings are inherited.
Returns a new cache instance that either respects or ignores sessions. Pass true to create a clone of the original instance, but ignore sessions. Passing false will create an instance that will include session IDs in generated cache keys. Passing no arguments is treated the same as passing true.
NOTE: If you supply a genCacheKey or withCacheKey option then this option
will not apply since you have chosen to generate cache keys manually.
Deletes all cache entries associated with this middleware namespace and fires the callback once complete. If you supply a string for the first parameter this will be passed to the flush function to target specific keys.
By default only HTTP GET requests with 200 responses are cached using the URL a
$ claude mcp add express-expeditious \
-- python -m otcore.mcp_server <graph>