Skip to content

wug-ge/express-redis-simple-cache

Repository files navigation

express-redis-simple-cache

npm version

A simple and effective Redis-based caching middleware for Express.js APIs.

This package provides a drop-in caching layer for your Express applications. It uses a Redis client under the hood to store and serve HTTP responses, helping you improve performance and reduce load on upstream APIs or databases. The design is intentionally minimal: you can enable caching on a per-route basis, decide when cached content expires, and choose how cache keys are generated.

Features

  • Simple integration – Wrap your Express routes with a single cache() middleware to enable caching on that route.
  • Customizable TTL – Specify how many seconds a response should remain cached; defaults to 60 seconds if omitted.
  • Flexible key generation – Cache keys can be constant (always), depend on the authenticated user (per-auth-token), depend on the full request URL (per-request-url), or depend on a custom cookie (per-custom-cookie). This makes it easy to cache shared resources or personalized content correctly.
  • Works with JSON and text responses – Responses are cached transparently. When there is a cache hit, the middleware adds an X-Cache: HIT header and returns the cached body.
  • Logging control – Choose between normal, debug, or silent logging to suit your environment.

Installation

Install the package and its peer dependency redis from npm. You’ll also need to have a Redis server running (local or remote).

npm install express-redis-simple-cache redis

Getting Started

The middleware exposes three functions:

Function Purpose
setupCache(redisClientOptions, logLevel) Initializes a Redis client using the provided options (host, port, password, etc.) and sets the log level. It must be called once at application startup before using the cache middleware.
cache(route: Route) Returns an Express middleware that applies caching based on the Route configuration. If no cache configuration is provided, it acts as a pass-through and simply calls next().
stopCache(redisClient) Closes the Redis connection. Call this when shutting down your server gracefully to free resources.

Route Configuration

A Route object declares the HTTP method, the route path, and an optional cache configuration:

type Route = {
  method: string;
  route: string;
  cache?: {
    expire?: number; // TTL in seconds (defaults to 60)
    type: 'always' | 'per-auth-token' | 'per-request-url';
  } | {
    expire?: number;
    type: 'per-custom-cookie';
    customCookie?: string; // name of your custom cookie
  };
};
  • expire – Optional number of seconds before the cached value is evicted. Defaults to 60 seconds if omitted.
  • type – Controls how the cache key is created:
    • always – Uses the method and route only (e.g., cache:GET:/users), so all clients share the same cached content.
    • per-auth-token – Includes a cookie or authorization header value in the key; if no auth token is found, the cache is bypassed.
    • per-request-url – Uses the full request URL; useful when query parameters affect the response.
    • per-custom-cookie – Includes the value of a specific cookie in the key. If the cookie is missing, caching is skipped.

Example Usage in Express

Below is a complete example illustrating how to use express-redis-simple-cache in a basic Express server:

import express from 'express';
import { setupCache, cache, stopCache } from 'express-redis-simple-cache';

async function startServer() {
  const redisClient = await setupCache({
    url: 'redis://localhost:6379',
  }, 'normal');

  const app = express();

  app.get('/users', cache({
    method: 'GET',
    route: '/users',
    cache: { expire: 300, type: 'always' },
  }), async (req, res) => {
    const users = await fetchUsersFromDatabase();
    res.json(users);
  });

  app.get('/profile', cache({
    method: 'GET',
    route: '/profile',
    cache: { expire: 120, type: 'per-auth-token' },
  }), async (req, res) => {
    const profile = await getCurrentUserProfile(req);
    res.json(profile);
  });

  app.get('/search', cache({
    method: 'GET',
    route: '/search',
    cache: { expire: 60, type: 'per-request-url' },
  }), async (req, res) => {
    const results = await searchProducts(req.query.q);
    res.json(results);
  });

  app.get('/cart', cache({
    method: 'GET',
    route: '/cart',
    cache: { expire: 600, type: 'per-custom-cookie', customCookie: 'cartId' },
  }), async (req, res) => {
    const cart = await fetchCart(req.cookies.cartId);
    res.json(cart);
  });

  const server = app.listen(3000, () => {
    console.log('Server listening on port 3000');
  });

  process.on('SIGINT', async () => {
    await stopCache(redisClient);
    server.close(() => process.exit(0));
  });
}

startServer().catch((err) => {
  console.error(err);
});

async function fetchUsersFromDatabase() {
  return [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];
}

async function getCurrentUserProfile(req) {
  return { id: 1, name: 'Alice', email: '[email protected]' };
}

async function searchProducts(q) {
  return [];
}

async function fetchCart(cartId) {
  return { items: [] };
}

Tips and Best Practices

  • Call setupCache once at application start and reuse the returned client. If setupCache isn’t called or if the Redis client isn’t ready, the middleware logs an error and bypasses caching.
  • To clear the cache manually (e.g., after data changes), use the redisClient returned by setupCache() to call del on specific keys or flushall() to clear everything.
  • Use type: 'per-request-url' for endpoints where query parameters affect the response (e.g., search endpoints) so that each unique URL is cached separately.
  • Choose an appropriate expire time. Shorter TTLs keep data fresh but may increase load on your backend; longer TTLs provide better performance but may return slightly stale data.
  • Use the debug log level during development to trace cache hits and misses.

Additional Notes on per-auth-token and Cookie-Based Caching

How per-auth-token Works

When using type: 'per-auth-token', the middleware attempts to extract an authentication token to include in the cache key. It looks for the token in the following locations, in order:

  1. Authorization Header: The middleware checks for a Bearer token in the Authorization header (e.g., Authorization: Bearer <token>). All headers being checked:
  • Authorization
  • X-Auth-Token
  • X-Access-Token
  • X-Refresh-Token
  • X-ID-Token
  • X-API-key
  • Token
  • Auth-Token
  1. Cookies: The middleware looks for the following cookies, in order:
  • authToken
  • accessToken
  • refreshToken
  • idToken
  • jwt
  • token
  • sessionToken
  • auth_token
  • access_token
  • refresh_token
  • bearer_token

If none of these are present, caching is bypassed for the request.

Cookie-Based Caching Requirements

For caching types that rely on cookies (e.g., per-auth-token or per-custom-cookie), you must use a middleware that populates req.cookies. One popular choice is the cookie-parser package. Install it with:

npm install cookie-parser

Then, include it in your Express application before using the caching middleware:

import cookieParser from 'cookie-parser';

app.use(cookieParser());

This ensures that req.cookies is populated, allowing the caching middleware to function correctly when relying on cookies.

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

A simple and effective Redis-based caching middleware for Express.js APIs.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •