• This repository has been archived on 20/Mar/2024
  • Stars
    star
    290
  • Rank 142,981 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🔌 A cache middleware for https://strapi.io

Strapi LRU Caching middleware - Legacy

A cache middleware for the headless CMS strapi.io

NO LONGER MAINTAINED

THIS PROJECT IS DEPRECATED

Since the release of Strapi v4, this project has been parked in favor of the new Strapi Plugin Rest Cache which was forked from this repository. Maintainers have now moved there, and we recommend you switch to the new and improved plugin. A big thank you to all those that contributed to this middleware, it has served many.

TOC

Strapi V4 users: https://strapi-community.github.io/strapi-plugin-rest-cache/

How it works

This middleware caches incoming GET requests on the strapi API, based on query params and model ID. The cache is automatically busted everytime a PUT, PATCH, POST, or DELETE request comes in.

Supported storage engines

  • Memory (default)
  • Redis

Important: Caching must be explicitely enabled per model

Installing

Using npm

npm install --save strapi-middleware-cache

Using yarn

yarn add strapi-middleware-cache

Version 1 compatibility

⚠️ Important: The middleware has gone through a full rewrite since version 1, and its configuration may not be fully compatible with the old v1. Make sure to (re)read the documentation below on how to use it 👇

Requirements

Since 2.0.1:

  • strapi 3.4.0
  • node 14

See 1.5.0 for strapi < 3.4.0

Setup

For Strapi stable versions, add a middleware.js file within your config folder

e.g

touch config/middleware.js

To use different settings per environment, see the Strapi docs for environments.

You can parse environment variables for the config here as well if you wish to, please see the Strapi docs for environment variables.

Enable the cache middleware by adding the following snippet to an empty middleware file or simply add in the settings from the below example:

module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
    },
  },
});

Starting the CMS should now log the following

$ strapi develop
[2021-02-26T07:03:18.981Z] info [cache] Mounting LRU cache middleware
[2021-02-26T07:03:18.982Z] info [cache] Storage engine: mem

Configure models

The middleware will only cache models which have been explicitely enabled. Add a list of models to enable to the module's configuration object.

e.g

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      models: ['review'],
    },
  },
});

Starting the CMS should now log the following

$ strapi develop
[2021-02-26T07:03:18.981Z] info [cache] Mounting LRU cache middleware
[2021-02-26T07:03:18.982Z] info [cache] Storage engine: mem
[2021-02-26T07:03:18.985Z] debug [cache] Register review routes middlewares
[2021-02-26T07:03:18.986Z] debug [cache] POST /reviews purge
[2021-02-26T07:03:18.987Z] debug [cache] DELETE /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] PUT /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] GET /reviews recv maxAge=3600000
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/:id recv maxAge=3600000
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/count recv maxAge=3600000

Configure the storage engine

The module's configuration object supports the following properties

Example

With Redis Sentinels

// config/middleware.js

/**
 * @typedef {import('strapi-middleware-cache').UserMiddlewareCacheConfig} UserMiddlewareCacheConfig
 */

module.exports = ({ env }) => ({
  settings: {
    /**
     * @type {UserMiddlewareCacheConfig}
     */
    cache: {
      enabled: true,
      type: 'redis',
      models: ['review'],
      redisConfig: {
        sentinels: [
          { host: '192.168.10.41', port: 26379 },
          { host: '192.168.10.42', port: 26379 },
          { host: '192.168.10.43', port: 26379 },
        ],
        name: 'redis-primary',
      },
    },
  },
});

With Redis Cluster

// config/middleware.js

/**
 * @typedef {import('strapi-middleware-cache').UserMiddlewareCacheConfig} UserMiddlewareCacheConfig
 */

module.exports = ({ env }) => ({
  settings: {
    /**
     * @type {UserMiddlewareCacheConfig}
     */
    cache: {
      enabled: true,
      type: 'redis',
      models: ['review'],
      redisConfig: {
        cluster: [
          { host: '192.168.10.41', port: 6379 },
          { host: '192.168.10.42', port: 6379 },
          { host: '192.168.10.43', port: 6379 },
        ],
      },
    },
  },
});

Running the CMS will output the following

$ strapi develop
[2021-02-26T07:03:18.981Z] info [cache] Mounting LRU cache middleware
[2021-02-26T07:03:18.982Z] info [cache] Storage engine: mem
[2021-02-26T07:03:18.985Z] debug [cache] Register review routes middlewares
[2021-02-26T07:03:18.986Z] debug [cache] POST /reviews purge
[2021-02-26T07:03:18.987Z] debug [cache] DELETE /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] PUT /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] GET /reviews recv maxAge=3600000
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/:id recv maxAge=3600000
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/count recv maxAge=3600000

Per-Model Configuration

Each route can hold its own configuration object for more granular control. This can be done simply by replacing the model strings in the list by object.

On which you can set:

  • Its own custom maxAge
  • Headers to include in the cache-key (e.g the body may differ depending on the language requested)

e.g

// config/middleware.js

/**
 * @typedef {import('strapi-middleware-cache').UserModelCacheConfig} UserModelCacheConfig
 */

module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      type: 'redis',
      maxAge: 3600000,
      models: [
        /**
         * @type {UserModelCacheConfig}
         */
        {
          model: 'reviews',
          headers: ['accept-language']
          maxAge: 1000000,
          routes: [
            '/reviews/:slug',
            '/reviews/:id/custom-route',
            { path: '/reviews/:slug', method: 'DELETE' },
          ]
        },
      ]
    }
  }
});

Running the CMS should now show the following

$ strapi develop
[2021-02-26T07:03:18.981Z] info [cache] Mounting LRU cache middleware
[2021-02-26T07:03:18.982Z] info [cache] Storage engine: mem
[2021-02-26T07:03:18.985Z] debug [cache] Register review routes middlewares
[2021-02-26T07:03:18.986Z] debug [cache] POST /reviews purge
[2021-02-26T07:03:18.987Z] debug [cache] DELETE /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] PUT /reviews/:id purge
[2021-02-26T07:03:18.987Z] debug [cache] GET /reviews recv maxAge=1000000 vary=accept-language
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/:id recv maxAge=1000000 vary=accept-language
[2021-02-26T07:03:18.988Z] debug [cache] GET /reviews/count recv maxAge=1000000 vary=accept-language
[2021-02-26T07:03:18.990Z] debug [cache] GET /reviews/:slug maxAge=1000000 vary=accept-language
[2021-02-26T07:03:18.990Z] debug [cache] GET /reviews/:id/custom-route maxAge=1000000 vary=accept-language
[2021-02-26T07:03:18.990Z] debug [cache] DELETE /reviews/:slug purge

Single types

By default, the middleware assumes that the specified models are collections. Meaning that having 'post' or 'posts' in your configuration will result in the /posts/* being cached. Pluralization is applied in order to match the Strapi generated endpoints.

That behaviour is however not desired for single types such as homepage which should remain singular in the endpoint (/homepage)

You can mark a specific model as being a single type by using the singleType boolean field on model configurations

e.g

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      models: [
        {
          model: 'footer',
          singleType: true,
        },
      ],
    },
  },
});

Running the CMS should now show the following

$ strapi develop
[2021-02-26T07:03:18.981Z] info [cache] Mounting LRU cache middleware
[2021-02-26T07:03:18.982Z] info [cache] Storage engine: mem
[2021-02-26T07:03:18.985Z] debug [cache] Register review routes middlewares
[2021-02-26T07:03:18.986Z] debug [cache] PUT /footer purge
[2021-02-26T07:03:18.987Z] debug [cache] DELETE /footer purge
[2021-02-26T07:03:18.987Z] debug [cache] GET /review recv maxAge=3600000

Authentication

By default, cache is not looked up if Authorization or Cookie header are present. To dissable this behaviour add hitpass: false to the model cache configuration

You can customize event further with a function hitpass: (ctx) => true where ctx is the koa context of the request. Keep in mind that this function is executed before every recv requests.

e.g

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      models: [
        {
          model: 'footer',
          hitpass: false,
          singleType: true,
        },
      ],
    },
  },
});

Etag support

By setting the enableEtagSupport to true, the middleware will automatically create an Etag for each payload it caches.

Further requests sent with the If-None-Match header will be returned a 304 Not Modified status if the content for that url has not changed.

Clearing related cache

By setting the clearRelatedCache to true, the middleware will inspect the Strapi models before a cache clearing operation to locate models that have relations with the queried model so that their cache is also cleared (this clears the whole cache for the related models). The inspection is performed by looking for direct relations between models and also by doing a deep dive in components, looking for relations to the queried model there too.

Cache entry point

Koa Context

By setting the withKoaContext configuration to true, the middleware will extend the Koa Context with an entry point which can be used to clear the cache from within controllers

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      withKoaContext: true,
      models: ['post'],
    },
  },
});

// controller

module.exports = {
  async index(ctx) {
    ctx.middleware.cache; // A direct access to the Cache API
  },
};

IMPORTANT: We do not recommend using this unless truly necessary. It is disabled by default as it goes against the non-intrusive/transparent nature of this middleware.

Strapi Middleware

By setting the withStrapiMiddleware configuration to true, the middleware will extend the Strapi middleware object with an entry point which can be used to clear the cache from anywhere (e.g., inside a Model's lifecycle hook where ctx is not available).

// config/middleware.js
module.exports = ({ env }) => ({
  settings: {
    cache: {
      enabled: true,
      withStrapiMiddleware: true,
      models: ['post'],
    },
  },
});

// model

module.exports = {
  lifecycles: {
    async beforeUpdate(params, data) {
      strapi.middleware.cache; // A direct access to the Cache API
    },
  },
};

IMPORTANT: We do not recommend using this unless truly necessary. It is disabled by default as it goes against the non-intrusive/transparent nature of this middleware.

Cache API

/**
 * @typedef {import('strapi-middleware-cache').CacheStore} CacheStore
 * @typedef {import('strapi-middleware-cache').MiddlewareCacheConfig} MiddlewareCacheConfig
 * @typedef {import('strapi-middleware-cache').ModelCacheConfig} ModelCacheConfig
 * @typedef {import('strapi-middleware-cache').CustomRoute} CustomRoute
 */

const cache = {
  /**
   * @type {CacheStore}
   */
  store,

  /**
   * @type {MiddlewareCacheConfig}
   */
  options,

  /**
   * Clear cache with uri parameters
   *
   * @param {ModelCacheConfig} cacheConf
   * @param {{ [key: string]: string; }=} params
   */
  clearCache,

  /**
   * Get related ModelCacheConfig
   *
   * @param {string} model
   * @param {string=} plugin
   * @returns {ModelCacheConfig=}
   */
  getCacheConfig,

  /**
   * Get related ModelCacheConfig with an uid
   *
   * uid:
   * - application::sport.sport
   * - plugins::users-permissions.user
   *
   * @param {string} uid
   * @returns {ModelCacheConfig=}
   */
  getCacheConfigByUid,

  /**
   * Get models uid that is related to a ModelCacheConfig
   *
   * @param {ModelCacheConfig} cacheConf The model used to find related caches to purge
   * @return {string[]} Array of related models uid
   */
  getRelatedModelsUid,

  /**
   * Get regexs to match all ModelCacheConfig keys with given params
   *
   * @param {ModelCacheConfig} cacheConf
   * @param {{ [key: string]: string; }=} params
   * @param {boolean=} wildcard
   * @returns {RegExp[]}
   */
  getCacheConfRegExp,

  /**
   * Get regexs to match CustomRoute keys with given params
   *
   * @param {CustomRoute} route
   * @param {{ [key: string]: string; }=} params
   * @param {boolean=} wildcard
   * @returns {RegExp[]}
   */
  getRouteRegExp,
};

Admin panel interactions

The strapi admin panel uses a separate rest api to apply changes to records, e.g /content-manager/explorer/application::post.post the middleware will also watch for write operations on that endpoint and bust the cache accordingly

More Repositories

1

love-animation

💚 A minimal Love2D animation library
Lua
31
star
2

lovelier

💚 A Love2D live reloader with Moonscript support
JavaScript
22
star
3

jsonapi-serializer-formats

💎 Gem to enrich jsonapi-serializer with multiple formats
Ruby
20
star
4

pocket-cms

☁️ A pocket sized CMS written for nodejs
JavaScript
16
star
5

jira-release-notes

🔀 Github action to generate release notes based on JIRA tickets
JavaScript
13
star
6

vitals

Publishing my vitals over the cloud
JavaScript
5
star
7

vue-reactive-clock

🔌 A vue plugin for creating time reactive UIs
JavaScript
3
star
8

relationship

A humble Love2D game skeleton
Lua
3
star
9

go-fullstack

The art of simple (but thriving) side projects 🚀
templ
3
star
10

resume.md

A markdown resume project
CSS
2
star
11

functional-chain

Mini engine to create a chainable function API
JavaScript
2
star
12

kankyo

🔧 A nicer environment-aware alternative to .env
TypeScript
2
star
13

lodash-patterns

Small pattern matching tool using lodash
TypeScript
2
star
14

fries-and-furious

A WP7 Rush made in one weekend !
C#
1
star
15

BatteryLiveWallpaper

A (very incomplete) live wallpaper featureing the battery level of the phone.Made on Android Studio 0.1
1
star
16

introspection-game

MoonScript
1
star
17

pocket-vue-seed

A pocket-cms seed project with a Vue front-end
JavaScript
1
star
18

pdf-render

A small webservice to generate PDFs out of webpages
JavaScript
1
star
19

triangle-portrait

Render an image using small triangles. Built with processing
Processing
1
star
20

capacitor.js

Functional utility method: a capacitor concept
JavaScript
1
star
21

tronic-drum-kit

Arduino powered LED drums
C++
1
star
22

demo-react-native

A demo application using the jsonplaceholder.typicode.com API
JavaScript
1
star