Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/etienne-martin/express-aggressive-cache

An aggressive yet obedient cache middleware for express
https://github.com/etienne-martin/express-aggressive-cache

api cache express json memory middleware redis response ssr

Last synced: about 1 month ago
JSON representation

An aggressive yet obedient cache middleware for express

Awesome Lists containing this project

README

        

# express aggressive cache

An aggressive yet obedient cache middleware for express.

[![Coveralls github](https://img.shields.io/coveralls/github/etienne-martin/express-aggressive-cache.svg)](https://coveralls.io/github/etienne-martin/express-aggressive-cache)
[![CircleCI build](https://img.shields.io/circleci/project/github/etienne-martin/express-aggressive-cache.svg)](https://circleci.com/gh/etienne-martin/express-aggressive-cache)
[![node version](https://img.shields.io/node/v/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)
[![npm version](https://img.shields.io/npm/v/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)
[![npm monthly downloads](https://img.shields.io/npm/dm/express-aggressive-cache.svg)](https://www.npmjs.com/package/express-aggressive-cache)

## Features

- Plug and Play
- Built-in TypeScript support
- Multiple data stores (in-memory and Redis)
- Thoroughly tested

## Getting Started

### Installation

To use express-aggressive-cache in your project, run:

```bash
npm install express-aggressive-cache
```

### Usage

**Example** - application-wide caching:

```javascript
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:

```javascript
import express from "express";
import cache from "express-aggressive-cache";

const app = express();

app.get("/hello", cache().middleware, (req, res) => {
res.json({
hello: "world"
});
});
```

## Cache Control

This middleware uses the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/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.

## Purge function

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`

```javascript
import cache from "express-aggressive-cache";

const myCache = cache()

// usage of myCache.middleware

const purgeEndpoint = async () => {
await myCache.purge("c341fbb1-a6f6-4a21-8949-c84017da27dd");
}

```

## x-cache

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.

## Options

#### `maxAge`

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.

#### `store`

Specify a different data store. Default to in-memory caching.

### `memoryStore(options)`

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.**

#### `max`

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:

```javascript
cache({
store: memoryStore({
max: 500
})
});
```

---

### `redisStore(options)`

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.**

#### `client`

An instance of Redis or a Redis compatible client.

Known compatible and tested clients:

- [ioredis](https://www.npmjs.com/package/ioredis)

#### `prefix`

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:

```javascript
cache({
store: redisStore({
client: new Redis("//localhost:6379"),
prefix: "api-cache"
})
});
```

---

#### `getCacheKey`

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:

```javascript
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:

```javascript
cache({
getCacheKey: ({ normalizedPath }) => {
const appVersion = process.env.npm_package_version;

return `${appVersion}:${normalizedPath}`;
}
});
```

#### `getCacheTag`

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:

```javascript
cache({
getCacheTag: ({ res }): string | undefined => {
return res.get("edge-cache-tag");
}
});
```

#### `onCacheHit` and `onCacheMiss`

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:

```javascript
const onCacheHit: OnCache = ({ req, res }) => {
res.setHeader("x-cache", "HIT");
};

const onCacheMiss: OnCache = ({ req, res }) => {
res.setHeader("x-cache", "MISS");
};
```

#### `debug`

A flag to toggle debug logs. Defaults to `false`.

## TypeScript

Type definitions are included in this library and exposed via:

```typescript
import {
ExpressAggressiveCacheOptions,
GetCacheKey
} from "express-aggressive-cache";
```

## Built with

- [node.js](https://nodejs.org/en/) - Cross-platform JavaScript run-time environment for executing JavaScript code server-side.
- [TypeScript](https://www.typescriptlang.org/) - Typed superset of JavaScript that compiles to plain JavaScript.
- [Jest](https://facebook.github.io/jest/) - Delightful JavaScript Testing.

## Contributing

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](https://github.com/etienne-martin/express-aggressive-cache/blob/master/README.md) with details of changes to the library.

Execute `yarn test` and update the tests if needed.

### Testing

Run the full test suite:

```bash
yarn test
```

Run tests in watch mode:

```bash
yarn test:watch
```

You can also run the following command to start the http server that is used when executing the tests:

```bash
yarn test:server
```

Will be accessible via http://localhost:3000

#### Local Redis Server

A local Redis instance is needed when running the test suite. You can use the provided [redis.sh](https://github.com/etienne-martin/express-aggressive-cache/blob/master/redis.sh) script to run a Redis container using docker (Make sure docker is installed and running).

```bash
./redis.sh
```

## Authors

- **Etienne Martin** - _Initial work_ - [etiennemartin.ca](http://etiennemartin.ca/)

## License

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