npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

express-redis-cache2

v2.0.0-beta7

Published

A module to make Express interact with Redis (create, get, delete). You can automatically cache all your most popular routes in Redis.

Downloads

65

Readme

Express Redis Cache 2

npm Build status Test coverage

Easily cache pages of your app using Express and Redis. Could be used without Express too.

Disclaimer: This library is a fork of express-redis-cache which appears to be no longer maintained as the last version was released 4 years ago and contains a potential exponential regex in monitor mode vulnerability.

Now express-redis-cache2 uses redis as an external dependency. Please see CHANGELOG.md.

Table of Contents

Install

express-redis-cache2 is compatible with redis@3.

npm install express-redis-cache2 redis@3

Usage

Just use it as a middleware in the stack of the route you want to cache.

const redis = require('redis');
const createExpressRedisCache = require('express-redis-cache2');

const client = redis.createClient();
const cache = createExpressRedisCache({ client });
const app = express();

// replace
app.get('/',
  (req, res) => { ... });

// by
app.get('/',
  cache.route(),
  (req, res) => { ... });

This will check if there is a cache entry for this route. If not. it will cache it and serve the cache next time route is called.

Redis Unavailability

Should the redis become unavailable, the express-redis-cache2 object will emit errors but will not crash the app. Express.js requests during this time will be bypass cache and will return fresh data.

Once the redis recovers, the caching will begin working again. See example code in the /example folder.

Name of the cache entry

By default, the cache entry name will be default prefix:name where name's value defaults to req.originalUrl.

app.get('/',
  cache.route(), // cache entry name is `cache.prefix + "/"`
  (req, res) => { ... });

You can specify a custom name like this:

app.get('/',
  cache.route('home'), // cache entry name is now `cache.prefix + "home"`
  (req, res) => { ... });

You can also use the object syntax:

app.get('/',
  cache.route({ name: 'home' }), // cache entry name is `cache.prefix + "home"`
  (req, res) => { ... });

Also, you can use res.express_redis_cache_name to specify the name of the entry such as:

app.get('/user/:userid',
  // middleware to define cache name
  (req, res, next) => {
    // set cache name
    res.express_redis_cache_name = 'user-' + req.params.userid;
    next();
  },

  // cache middleware
  cache.route(),

  // content middleware
  (req, res) => {
    res.render('user');
  }
);

Conditional caching

You can also use a previous middleware to set whether or not to use the cache by using res.use_express_redis_cache:

app.get('/user',
  // middleware to decide if using cache
  (req, res, next) => {
    // Use only cache if user not signed in
    res.use_express_redis_cache = ! req.signedCookies.user;

    next();
  }.

  // cache middleware
  cache.route(), // this will be skipped if user is signed in

  // content middleware
  (req, res) => {
    res.render('user');
  }
);

Prefix

All entry names are prepended by a prefix. Prefix is set when calling the Constructor.

const redis = require('redis');
const createExpressRedisCache = require('express-redis-cache2');

const client = redis.createClient();
// Set default prefix to "test". All entry names will begin by "test:"
const cache = createExpressRedisCache({
  client,
  prefix: 'test',
});

To know the prefix:

console.log('prefix', cache.prefix);

You can pass a custom prefix when calling route():

app.get('/index.html',
  cache.route('index', { prefix: 'test'  }), // force prefix to be "test", entry name will be "test:index"
  (req, res) => { ... });

You can also choose not to use prefixes:

app.get('/index.html',
  cache.route({ prefix: false  }), // no prefixing, entry name will be "/index.html"
  (req, res) => { ... });

Expiration

Unless specified otherwise when calling the Constructor, cache entries don't expire. You can specify a default lifetime when calling the constructor:

const redis = require('redis');
const createExpressRedisCache = require('express-redis-cache2');

const client = redis.createClient();
// Set default lifetime to 60 seconds for all entries
const cache = createExpressRedisCache({ client, expire: 60 });

You can overwrite the default lifetime when calling route():

app.get('/index.html',
  cache.route({ expire: 5000  }), // cache entry will live 5000 seconds
  (req, res) => { ... });

// You can also use the number sugar syntax
cache.route(5000);
// Or
cache.route('index', 5000);
// Or
cache.route({ prefix: 'test' }, 5000);

You can also provide a hash of status code / expiration values if you for example want to retry much sooner in failure cases (403/404/500/etc). Status ranges can be specified via 4xx/5xx. You must provide a default value (xxx). The most specific rule will be used. For example, if the status code is 200, and there are expirations set for 200, 2xx, and xxx, the expiration for 200 will be used.

app.get('/index.html',
  cache.route({
    expire: {
      200: 5000,
      4xx: 10,
      403: 5000,
      5xx: 10,
      xxx: 1
    }
  }),
  (req, res) => { ... });

You can also specify

Content Type

You can use express-redis-cache2 to cache HTML pages, CSS stylesheets, JSON objects, anything really. Content-types are saved along the cache body and are retrieved using res._headers['content-type']. If you want to overwrite that, you can pass a custom type.

app.get('/index.html',
  cache.route({ type: 'text/plain'  }), // force entry type to be "text/plain"
  (req, res) => { ... });

Events

The module emits the following events:

error

You can catch errors by adding a listener:

cache.on('error', function (error) {
  throw new Error('Cache error!');
});

message

express-redis-cache2 logs some information at runtime. You can access it like this:

cache.on('message', function (message) {
  // ...
});

connected

Emitted when the client is connected (or re-connected) to Redis server

cache.on('connected', () => {
  // ....
});

disconnected

Emitted when the client is disconnected from Redis server. When (and if) it reconnects, it will emit a 'connected' event again

cache.on('disconnected', () => {
  // ....
});

Note You can get the connexion status at any time by getting the property cache.connected which returns a boolean (true = connected, false = disconnected).

The Entry Model

This is the object synopsis we use to represent a cache entry:

const entry = {
  body:    String // the content of the cache
  touched: Number // last time cache was set (created or updated) as a Unix timestamp
  expire:  Number // the seconds cache entry lives (-1 if does not expire)
  type: String    // the content-type
};

The module

The module exposes a function which instantiates a new instance of a class called ExpressRedisCache.

// This
const cache = require('express-redis-cache2')({ /* ... */ });
// is the same than
const cache = new (require('express-redis-cache2/lib/ExpressRedisCache'))({ /* ... */ });

The constructor

As stated above, call the function exposed by the module to create a new instance of ExpressRedisCache,

const cache = require('express-redis-cache2')(options);

Where options is an object that has the following properties:

| Option | Type | Default | Description | | ----------- | ------------- | ------------------------------------------------------------- |------------------------------------------------------------ | | prefix | String | require('express-redis-cache2/package.json').config.prefix | Default prefix (This will be prepended to all entry names) | | expire | Number | undefined | Default life time of entries in seconds | | client | RedisClient | | A Redis client |

API

The route method is designed to integrate easily with Express. You can also define your own caching logics using the other methos of the API shown below.

get Get cache entries

cache.get(/** Mixed (optional) */ query, /** Function( Error, [Entry] ) */ callback );

To get all cache entries:

cache.get(function (error, entries) {
  if ( error ) throw error;

  entries.forEach(console.log.bind(console));
});

To get a specific entry:

cache.get('home', function (error, entries) {});

To get a specific entry for a given prefix:

cache.get({ name: 'home', prefix: 'test' }, function (error, entries) {});

You can use wildcard:

cache.get('user*', function (error, entries) {});

add Add a new cache entry

cache.add(/** String */ name, /** String */ body, /** Object (optional) **/ options, /** Function( Error, Entry ) */ callback )

Where options is an object that can have the following properties:

  • expire Number (lifetime of entry in seconds)
  • type String (the content-type)

Example:

cache.add('user:info', JSON.stringify({ id: 1, email: '[email protected]' }), { expire: 60 * 60 * 24, type: 'json' },
    function (error, added) {});

del Delete a cache entry

cache.del(/** String */ name, /** Function ( Error, Number deletions ) */ callback);

You can use wildcard (*) in name.

size Get cache size for all entries

cache.size(/** Function ( Error, Number bytes ) */);

Example Code

Run the example to see how the caching behaves. You can kill the redis-server and the application will respond with non-cached information.

yarn install
node example

Development Setup

  1. Install Nix Package Manager

  2. Install direnv with your OS package manager

  3. Hook it direnv into your shell

  4. At the top-level of your project run:

    direnv allow

    The next time your launch your terminal and enter the top-level of your project, direnv will check for changes.

  5. Install dependencies

    yarn install

License

Under the MIT license. See LICENSE file for more details.