Skip to content

Caching a content type

The plugin caches nothing until you list the content types it should cache. That list is strategy.contentTypes, and each entry is either a uid string or an object.

js
// file: ./config/plugins.js

module.exports = {
  "rest-cache": {
    config: {
      strategy: {
        contentTypes: [
          // Shorthand: cache this content type's default routes, using the
          // strategy-level maxAge, hitpass and keys.
          "api::article.article",

          // Full form, for anything that needs to differ.
          {
            contentType: "api::category.category",
            maxAge: 60000, // 1 minute, in milliseconds
          },
        ],
      },
    },
  },
};
ts
// file: ./config/plugins.ts

export default {
  "rest-cache": {
    config: {
      strategy: {
        contentTypes: [
          // Shorthand: cache this content type's default routes, using the
          // strategy-level maxAge, hitpass and keys.
          "api::article.article",

          // Full form, for anything that needs to differ.
          {
            contentType: "api::category.category",
            maxAge: 60000, // 1 minute, in milliseconds
          },
        ],
      },
    },
  },
};

A uid that does not resolve to a content type fails the boot with contentType uid "…" not found, rather than being ignored. A silently ignored typo is a cache that never caches and never says why.

Which routes this registers

With injectDefaultRoutes on (the default), the plugin looks at the API the content type belongs to and registers its default REST routes. The API prefix from api.rest.prefix (/api unless you changed it) is included.

For a collection type with plural name articles:

RouteCached
GET /api/articlesyes
GET /api/articles/:idyes
POST /api/articlesno — used only for route-based invalidation
PUT /api/articles/:idno — used only for route-based invalidation
DELETE /api/articles/:idno — used only for route-based invalidation

For a single type with singular name homepage:

RouteCached
GET /api/homepageyes
PUT /api/homepageno — used only for route-based invalidation
DELETE /api/homepageno — used only for route-based invalidation

Only GET responses are ever stored or looked up. The write routes appear in the resolved strategy because the legacy, route-based invalidation path needs them; with the default document-service invalidation they are inert. See Invalidation.

The :id segment is also what a targeted purge fills in — purging api::article.article with { id: "abc123" } clears /api/articles/abc123 and the collection route, without touching every other entry.

To confirm what was actually registered, open Settings → REST Cache: each content type lists its resolved routes. A route you expected and cannot find there was never registered.

Inheritance

maxAge, hitpass and keys exist at three levels and each inherits from the one above it:

strategy  →  content type  →  route

A content type that sets nothing behaves exactly like the strategy defaults. A content type that sets keys replaces the strategy's keys for itself and for every route it owns, unless a route sets its own.

js
// file: ./config/plugins.js

module.exports = {
  "rest-cache": {
    config: {
      strategy: {
        maxAge: 3600000, // 1 hour, in milliseconds
        keys: { useQueryParams: true, useHeaders: [] },

        contentTypes: [
          // Inherits both.
          "api::article.article",

          {
            // Prices change often and are the same for everyone.
            contentType: "api::product.product",
            maxAge: 30000, // 30 seconds
            keys: { useQueryParams: ["filters", "locale", "page"] },
          },
        ],
      },
    },
  },
};
ts
// file: ./config/plugins.ts

export default {
  "rest-cache": {
    config: {
      strategy: {
        maxAge: 3600000, // 1 hour, in milliseconds
        keys: { useQueryParams: true, useHeaders: [] },

        contentTypes: [
          // Inherits both.
          "api::article.article",

          {
            // Prices change often and are the same for everyone.
            contentType: "api::product.product",
            maxAge: 30000, // 30 seconds
            keys: { useQueryParams: ["filters", "locale", "page"] },
          },
        ],
      },
    },
  },
};

Content types without default routes

injectDefaultRoutes: false stops the plugin looking for default routes. You need it in two cases.

A content type owned by a plugin. Plugins do not have generated API routes, so there is nothing to inject; the plugin skips them automatically, and any route you want cached has to be listed explicitly.

A content type whose API has no routes, because you deleted or replaced them. The plugin raises

no API "<name>" found for contentType "<uid>".
Set "injectDefaultRoutes: false" for this contentType if it has no default routes.

rather than crashing later with something unrelated.

Content types not named after their API

The owning API is resolved from the uid, not from the content type's singular name. api::writer.editor lives in the writer API even though its singular name is editor. Deriving it from the name instead used to take the whole application down at register time with "Cannot read properties of undefined". (#125)

While resolving your configuration the plugin walks each content type's schema and records everything it can reach through relations and through components, following components into their own attributes until nothing new is found. That set is what clearRelatedCache uses: purging a content type also purges the cached entries of the content types related to it.

You do not configure this list — it is derived from your schemas — but it is worth knowing it exists, because it explains purges that look wider than the write that caused them. See Invalidation.

Caching authenticated responses

A content type can opt out of the default hitpass and cache authenticated traffic. If you do that, key entries on the caller as well:

js
// file: ./config/plugins.js

module.exports = {
  "rest-cache": {
    config: {
      strategy: {
        contentTypes: [
          {
            contentType: "api::order.order",
            hitpass: false, // cache even with an authorization header
            keys: { useAuth: true }, // ...but one entry per caller
          },
        ],
      },
    },
  },
};
ts
// file: ./config/plugins.ts

export default {
  "rest-cache": {
    config: {
      strategy: {
        contentTypes: [
          {
            contentType: "api::order.order",
            hitpass: false, // cache even with an authorization header
            keys: { useAuth: true }, // ...but one entry per caller
          },
        ],
      },
    },
  },
};

WARNING

hitpass: false without keys.useAuth means every authorised caller shares one entry. The server logs a warning at boot naming the content type. See Cache keys.

Next