Caching in Strapi with Strapi Middleware Cache
Tags: javascript, caching, cms, strapi
Published: 2021-07-13 14:07:43

Most firms strive to retrieve and store data faster. It is because no one wants an application that is slow or lags. When there's a lot of data in the database, it becomes challenging to fetch data from the database swiftly, which may cause your application to lag. To avoid this, most software firms cache data.

[Caching](https://masteringbackend.com/posts/caching-strategy-for-restful-api) is the process of storing data in a temporary storage area so that retrieving data is relatively more accessible and faster. 

The idea of Caching aims to increase data retrieval performance by reducing the need to access the underlying slower storage layer. For Trading off capacity for speed, a cache typically stores a subset of data transiently, in contrast to databases whose data is usually complete and durable.

Strapi middleware cache is a middleware that caches incoming `GET` requests on the Strapi API based on query params and model ID. This middleware allows you to either cache your data on your application's memory by default or with Redis, a third-party database. The cache is automatically busted every time a `PUT`, `POST`, or `DELETE` request comes in.

This article will explore the Strapi middleware cache and how to cache data in our Strapi application.

## Why is Caching Necessary

Just as I emphasized in my opening statement, time is of great essence when dealing with data. Most software stores their data in a database, which may have different storage layers. Because of the different layers of storage in the database and how large the data is, it becomes difficult to retrieve data faster. 

Although users must have accessed the same information earlier, it doesn't get displayed more quickly when they re-access them. 

To make fetching data easier, developers use a caching system. A caching system is a high-speed data storage layer, which stores a batch of data from the database. 

Whenever a user retrieves information from the database, the data is stored in a cache to increase data retrieval performance instead of accessing data from the different storage layers whenever a user requests them. 

The cache doesn't contain all of the data in the database, just a fraction of the data. In most cases, data previously accessed by the users.

Below are some importance of caching and why most software firms incorporate it into their application:

1. Caching helps to improve the performance of an application since the retrieval of data is faster and more efficient.
2. Caching reduces the load in your backend database as your application's frontend doesn't always have to send a request to your backend whenever a user requests data. It prevents poor performance or even crashes in times of spikes.
3. Caching eliminates database hotspots that occur due to users frequently asking a subset of data.

## Strapi Middleware Cache Installation and Setup

To use the Strapi middleware cache, I assume the following prerequisites:


1. You should install the required version of Node.js in your working environment.
2. You should understand the basic knowledge of how Strapi works.

First, you'll be installing the Strapi middleware cache plugin into your Strapi project by running the command below in your CLI from your project's location.

```
    npm install --save strapi-middleware-cache
    #or
    yarn add strapi-middleware-cache
```


Once you have the plugin set up in your local environment, add it to your Strapi project. Create a Strapi project. If you don't have one running, add a `middleware.js` file to your project's configuration. To do this, create a `middleware.js` file in your `config` folder, which is at the root of your project.

Enable the middleware cache plugin by putting the code below into the `middleware.js` file you just created. You can configure environments by following the guide on [Strapi's documentation](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/configurations.html#environment).

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


Start your Strapi project with `strapi develop` to see that the middleware cache plugin has been set up successfully in your working environment.

```
    $ strapi develop
    [2021-06-26T06:15:50.394Z] debug [Cache] Mounting LRU cache middleware
    [2021-06-26T06:15:50.396Z] debug [Cache] Storage engine: mem
```


## Model Configuration

After setting up the middleware cache plugin with our Strapi, we need to explicitly tell the plugin which model we'll want to apply to the cache. Let's add models to the middleware configuration object.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          models: ['blogs'],## you can add other models
        }
      }
    });
```

If you run your project with `strapi develop`, you’ll notice that caching for the `blogs` route has commenced.

```
    $ strapi develop
    [2021-06-26T20:25:14.744Z] debug [Cache] Mounting LRU cache middleware
    [2021-06-26T20:25:14.745Z] debug [Cache] Storage engine: mem
    [2021-06-26T20:25:14.752Z] debug [Cache] Caching route /blogs/:id* [maxAge=3600000]
```

## Storage Engine Configuration

Strapi middleware cache plugin uses memory by default to cache data. However, you can configure `Redis` as the cache engine. Sometimes, you'll want to configure the max number of entries, cache timeout, etc., in your storage engine. 

To do this, you'll have to specify the needed configurations under `settings` in your middleware configuration object. For example, I can configure the type of engine for caching as Redis and set `maxAge` and other properties of our models.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          type: 'redis',
          maxAge: 2600000,
          models: ['blogs'],
        }
      }
    });
```

Storage engine configuration can take the following properties:

**Type**
The `type` property describes the type of storage engine for the middleware cache plugin to use. By default, the `type` is set to memory which is denoted by `mem`.

**Max**
Max denotes the maximum number of entries that the cache can take in. By default, this number is set to 500.

**Max Age**
The `maxAge` tells us the time after which a cache entry will be considered invalid. This time is usually represented in milliseconds. The default `maxAge` in the `strapi-middleware-cache` is 3600000 milliseconds.

**Cache Timeout**
Sometimes, due to network problems caching data takes a longer time. Strapi-middleware-cache has a `cacheTimeout` which specifies the time after which a cache request is timed out.

**Enable Etag Support**
[Etag](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwiwp93w2LTxAhVHyxoKHWMnAycQFjAJegQIBRAD&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FHTTP_ETag&usg=AOvVaw1CJyQA7URK06_wHTDN8Rkh) (entity tag) headers are identifiers for a specific version of data. ETags prevents updates of a resource that could be simultaneous from overwriting each other. By default, the property `enableEtagSupport` is set to false.

**Logs**
Strapi-middleware-cache logs output in your console. For instance, when we first ran our project after configuring the middleware, we got the output below.

```

    [2021-06-26T06:15:50.394Z] debug [Cache] Mounting LRU cache middleware
    [2021-06-26T06:15:50.396Z] debug [Cache] Storage engine: mem
```


By default, the property `log` is set to `true`. If you set the property to `false`, you won't get an output in your console concerning the middleware.

**Populate Context**
The property `populateContext` is set to `false` by default. If you set the property to `true`, this setting will inject a cache entry point into the [Koa context](https://github.com/koajs/koa/blob/master/docs/api/context.md). It is advantageous, especially for developers who are interested in building with [Koa](https://koajs.com/).

**Headers**
For individual model configuration, you can set headers (**Cache**-**Control Header**) to specify caching policies. For instance, we can set headers for our model to accept and cache JavaScript only.

```
      {
        model: 'account',
        headers: ['accept-JavaScript']
      }
```


You can write different policies for your headers like `no-cache`, `no-store`, `public` and `private`.

**Redis Config** ***(redis only)***
For developers who don't want to use the default storage engine: `memory` and opt for `Redis`, they can configure Redis with a config object passed to [ioredis](https://www.npmjs.com/package/ioredis). For instance, I can configure other properties and configure Redis sentinel to monitor some of my nodes if there's a failover.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          type: 'redis',
          maxAge: 2600000,
          max: 400,
          cacheTimeout: 400,
          enableEtagSupport: true,
          logs: true,
          populateContext: false,
          models: ['blos'],
          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',
          }
        //or you can connect to redis lab with the command below.
         redisConfig: {
            host: 'redis-5555.c8.us-east-1-4.ec2.cloud.redislabs.com',
            port: 5555,
            password: 'secret_password',
          },
        }
      }
    });
```

If you don't configure your storage engines, they'll take the default settings. The default settings for storage engines are shown below.

![Capture d’écran 2021-07-13 à 15.20.52.png](/uploads/Capture_d_ecran_2021_07_13_a_15_20_52_5150d28b83.png)


## Configuring Each Model Separately

You can configure the properties of each model individually. For developers with a lot of models, this step is always helpful. 

For instance, you'll want to configure the `maxAge` or `cacheTimeout` property individually since the models will be accessing different types of resources. To configure model properties individually, but the property of interest into the model property as shown below.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          models: [
            {
              model: 'blogs',
              maxAge: 1000000,
              cacheTimeout: 400,
            },
            {
              model: 'posts',
              maxAge: 800000,
              cacheTimeout: 450,
            }
          ]
        }
      }
    });
```


From the example above, we have configured the `maxAge` and `cacheTimeout` properties for `blogs` and `posts` individually. Since we didn't configure other properties, `blogs` and `posts` will follow the default configuration for other properties. 

We can also set other properties in the `cache` object, which will cause `blogs` and `posts` to take those configurations instead of the default configuration. For instance, from the example below, our storage engine for `blogs` and `posts` will be Redis, and we won't have log outputs in our console.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          type: 'redis',
          logs: false,
          enabled: true,
          models: [
            {
              model: 'blogs',
              maxAge: 1000000,
              cacheTimeout: 400,
            },
            {
              model: 'posts',
              maxAge: 800000,
              cacheTimeout: 450,
            }
          ]
        }
      }
    });
```


**Pluralization and Single Types**

By default, when you create a content type in Strapi, your collection name is pluralized. For instance, if you create a `blog` content type, the collection name is `blogs`. What the Strapi-middleware-cache does is pluralize the model name in the configuration. So, if you put your model name as `posts` or `post` the middleware will cache `/posts/*`.

For Strapi [single types](https://strapi.io/blog/beta-19-single-types-uid-field) like `about`, this is not beneficial and can lead to an error. Furthermore, to make the middleware skip pluralization for single types, set `singleType` to `true` for your model.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          models: [
            {
              model: 'about',
              singleType: true,
            }
          ]
        }
      }
    });
```


Caching is essential, especially if you have an extensive database with different storage layers. Notice that with each `PUT`, `POST`, or `DELETE` request, the cache automatically gets busted. 

Busting a cache is crucial because it enables updates to keep happening in the cache even when using it. So, when the user updates, deletes or adds new data to their model, the cache is updated accordingly.

Also, you can clear your cache by setting the `populateContext` configuration to `true`. This setting will give you direct access to the cache engine. To do this, we will add a controller directly under our middleware module object. The middleware will extend the Koa Context with an entry point to clear the cache from within controllers.

```
    module.exports = ({ env }) => ({
      settings: {
        cache: {
          enabled: true,
          populateContext: true,
          models: ['blogs']
        }
      }
    });

    
    // controller
    
    module.exports = {
      async index(ctx) {
        ctx.middleware.cache.store // This will give a direct access to the cache engine
        await ctx.middleware.cache.bust({ model: 'blogs', id: '1' }); // Clear cache for this specific record
        await ctx.middleware.cache.bust({ model: 'posts' }); // Clear cache for the entire model collection
        await ctx.middleware.cache.bust({ model: 'about' }); // Don't pluralize model names of single types
      }
    };

```
    


## Conclusion

In this article, we have explored caching in Strapi with the strapi-middleware cache. We first discussed [caching and why it is necessary](https://masteringbackend.com/posts/caching-strategy-for-restful-api) to add caching to your application. Next, we looked at the strapi-middleware cache and installed and set it up in our working environment. Then we explored the different configurations and properties for the strapi-middleware cache.