Skip to main content
🔥
Guides

Getting Started | @express-route-cache

Step-by-step guide to setting up production-grade route caching for Express.js in under a minute. Covers installation, quick start, Redis, SWR, and cache invalidation.

Getting Started

Installation

Install the core package along with any adapters you need:

# Core package (includes Memory adapter)
npm install @express-route-cache/core

# Redis adapter (recommended for production)
npm install @express-route-cache/redis ioredis

# Memcached adapter
npm install @express-route-cache/memcached memjs

Quick Start

Setting up @express-route-cache takes less than a minute.

import express from "express";
import { createCache, createMemoryAdapter } from "@express-route-cache/core";

const app = express();

// 1. Initialize the Cache
const cache = createCache({
  adapter: createMemoryAdapter(),
  staleTime: 60, // Fresh for 60 seconds
  gcTime: 300, // Kept stale for 5 more minutes
  swr: true, // Enable Stale-While-Revalidate
});

// 2. Cache globally (GET requests only)
app.use(cache.middleware());

// 3. Or override per-route
app.get("/users/:id", cache.route({ staleTime: 120 }), (req, res) => {
  res.json({ id: req.params.id, name: "John Doe" });
});

// 4. Invalidate instantly upon mutation
app.post("/users", cache.invalidate("/users"), (req, res) => {
  // logic to create user...
  res.status(201).json({ success: true });
});

// 5. Manual Data Caching (Standalone Fetch)
const data = await cache.fetch(
  "users-list",
  async () => {
    return [{ id: 1, name: "John" }];
  },
  { swr: true, retry: 3 },
);

app.listen(3000);

Key Concepts

Invalidation

Invalidation is O(1). Instead of searching for keys to delete, we use "Epoch Versioning". Incrementing a version number makes all old cache entries instantly obsolete. See Epoch Invalidation for details.

🛠️ Advanced Patterns

1. Custom Cache Keys

Sometimes req.path isn't enough. You can override the key manually:

// Static key
cache.route({ key: "my-custom-key" });

// Dynamic key based on request (e.g., user-specific)
cache.route({ key: (req) => `user-${req.user.id}-profile` });

2. Header-based Caching (vary)

If your API returns different data based on headers (like Authorization or Accept-Language), use the vary option:

cache.route({ vary: ["Authorization", "Accept-Language"] });

3. Query Parameter Determinism

Avoid cache fragmentation by sorting query parameters:

// ?b=2&a=1 will be treated the same as ?a=1&b=2
cache.route({ sortQuery: true });

4. Automatic Invalidation

You can tell the cache to automatically increment the version for a route pattern whenever a successful POST, PUT, PATCH, or DELETE request is made:

// Globally
const cache = createCache({ autoInvalidate: true, ... });

// Or per-route
app.post('/users', cache.route({ autoInvalidate: true }), createUser);

5. Memory Protection (maxBodySize)

To prevent large responses (like 500MB videos) from crashing your Node process or filling up Redis, use maxBodySize. Any response larger than this will simply skip the cache.

cache.route({ maxBodySize: 1024 * 1024 * 5 }); // 5MB limit

6. Standalone Data Caching

cache.fetch() lets you cache any async data — not just Express routes — with SWR, Stampede Protection, and retry support:

const users = await cache.fetch("all-users", () => db.users.findMany(), {
  staleTime: 60,
  swr: true,
  retry: 2,
});

See the Standalone Fetch guide for full details.


What's Next?

TopicLink
Full worked example with GET/POST/PATCH/DELETEExample: Todo API
Real-world patterns (per-user, webhooks, warming)Recipes
Production: Docker, K8s, health checksDeployment
Testing your cached routesTesting
Understanding fresh/stale windows visuallySWR Concepts
How O(1) invalidation worksEpoch Invalidation
Building a custom adapterCacheClient Interface