An aggressive yet obedient cache middleware for express.
- Plug and Play
- Built-in TypeScript support
- Multiple data stores (in-memory and Redis)
- Thoroughly tested
To use express-aggressive-cache in your project, run:
npm install express-aggressive-cache
Example - application-wide caching:
import express from "express";
import cache from "express-aggressive-cache";
const app = express();
app.use(
cache({
maxAge: 3600
}).middleware
);
app.get("/hello", (req, res) => {
res.json({
hello: "world"
});
});
Example - caches a specific endpoint:
import express from "express";
import cache from "express-aggressive-cache";
const app = express();
app.get("/hello", cache().middleware, (req, res) => {
res.json({
hello: "world"
});
});
This middleware uses the Cache-Control header to determine whether or not a response should be cached and for how long.
You can specify for how long a response should be cached by specifying a max-age
or s-maxage
with a value greater than zero.
Responses that do not specify a max-age
or s-maxage
will be cached by default.
Responses containing no-store
, private
, max-age=0
or s-maxage=0
won't be cached. Anything else will be cached.
The s-maxage
directive always takes precedence over the max-age
directive.
The cache object provides an async purge function to remove a cache entry based on a previously passed cache tag. See the getCacheTag
option. The cache entry associated with the cache tag will be removed immediately. It will take at least a minute for all memory associated with the cache entry to be freed.
Example - purge the cache entry for a unique cache tag previously returned by getCacheTag
import cache from "express-aggressive-cache";
const myCache = cache()
// usage of myCache.middleware
const purgeEndpoint = async () => {
await myCache.purge("c341fbb1-a6f6-4a21-8949-c84017da27dd");
}
By default, the middleware will set a response header x-cache
. Its value will be HIT
when a cache hit occurs and the response was obtained from cache otherwise it will be MISS
. See the onCacheHit
and onCacheMiss
options to override this behavior.
If the response has a max-age
header, it will use it as the TTL. Otherwise, it will expire the resource using the maxAge
option (defaults to Infinity). Value should be provided in seconds.
Specify a different data store. Default to in-memory caching.
By default, the cache will be stored in memory (RAM). Since everything is stored in memory, the more cache, the higher the RAM usage. You can use the max
option to mitigate this. It will delete the least-recently-used items as it reaches the limit.
Note: In-memory caching is not suitable for applications that scale horizontally as the cache will be duplicated across multiple nodes and can result in a high memory usage within your cluster.
We recommend using a Redis data store if you have multiple instances of your application running behind a load balancer.
The maximum size of the cache, checked by applying the length function to all values in the cache. Defaults to Infinity
.
Example - limit the amount of entries in the cache:
cache({
store: memoryStore({
max: 500
})
});
It is recommended that Redis be configured with a allkeys-lru
eviction policy to prevent random keys from being deleted while serving responses.
Note: performance will be impacted when caching large responses like files and images. We do not recommend caching anything above 5mb.
An instance of Redis or a Redis compatible client.
Known compatible and tested clients:
Key prefix in Redis (default: "cache").
This prefix appends to whatever prefix you may have set on the client itself.
Note: You may need unique prefixes for different applications sharing the same Redis instance.
Example - store the cache in redis:
cache({
store: redisStore({
client: new Redis("//localhost:6379"),
prefix: "api-cache"
})
});
Function used to generate cache keys.
It determines how the cache key should be computed, receiving req
, res
and normalizedPath
as input.
Can be useful if you need to cache multiple variants of the same resource depending on the specifics of your application.
Example - cache authenticated and non-authenticated requests separately:
cache({
getCacheKey: ({ req, normalizedPath }) => {
const isAuthenticated = !!req.session.user;
return `${isAuthenticated}:${normalizedPath}`;
}
});
Example - invalidate the cache when pushing a new version of your app/api:
cache({
getCacheKey: ({ normalizedPath }) => {
const appVersion = process.env.npm_package_version;
return `${appVersion}:${normalizedPath}`;
}
});
Function to provide the purge tag which will be associated to the cache entry. The tag can later be used with the cache purge
function. The cache tag should be unique for the cache entry. If not, only the latest cache entry will be purgeable.
It receives req
and res
as input. It should return undefined
if there is no tag for the cache entry.
Example - Sample based on Akamai's Edge-Cache-Tag
response header:
cache({
getCacheTag: ({ res }): string | undefined => {
return res.get("edge-cache-tag");
}
});
Functions to perform a behavior on a cache hit or miss. For example: set a response header.
If not passed, the following default functions are used:
const onCacheHit: OnCache = ({ req, res }) => {
res.setHeader("x-cache", "HIT");
};
const onCacheMiss: OnCache = ({ req, res }) => {
res.setHeader("x-cache", "MISS");
};
A flag to toggle debug logs. Defaults to false
.
Type definitions are included in this library and exposed via:
import {
ExpressAggressiveCacheOptions,
GetCacheKey
} from "express-aggressive-cache";
- node.js - Cross-platform JavaScript run-time environment for executing JavaScript code server-side.
- TypeScript - Typed superset of JavaScript that compiles to plain JavaScript.
- Jest - Delightful JavaScript Testing.
When contributing to this project, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
Update the README.md with details of changes to the library.
Execute yarn test
and update the tests if needed.
Run the full test suite:
yarn test
Run tests in watch mode:
yarn test:watch
You can also run the following command to start the http server that is used when executing the tests:
yarn test:server
Will be accessible via http://localhost:3000
A local Redis instance is needed when running the test suite. You can use the provided redis.sh script to run a Redis container using docker (Make sure docker is installed and running).
./redis.sh
- Etienne Martin - Initial work - etiennemartin.ca
This project is licensed under the MIT License - see the LICENSE file for details.