Vue Storefront is now Alokai! Learn More
Caching API responses

Caching API responses

Modern CDNs can cache API responses, significantly reducing the load on your middleware and improving the performance of your application, even if the HTML itself is not cached. Please note, that due to the nature of the CDN cache, only the traffic between the browser and the middleware will be cached. The requests sent during the SSR process will not be cached.

Requirements

@vue-storefront/next in version 2.1.0 or higher

@vue-storefront/sdk in version 3.1.0 or higher

How to enable caching API responses

Set Cache-Control header in the middleware

1

Install the extension that provides control over response headers from the middleware:

yarn add @vsf-enterprise/middleware-headers

2

Setup the extension inside: apps/storefront-middleware/integrations/<integration>/extensions/cdn.ts

Repeat the process for all the integrations that you want to cache.

+import middlewareHeaders from "@vsf-enterprise/middleware-headers";
+
+const DEFAULT_MIDDLEWARE_TTL = 60 * 5;
+const cacheControl = process.env.CACHE_CONTROL || `public, max-age=0, s-maxage=${DEFAULT_MIDDLEWARE_TTL}, must-revalidate`
+
+export const cdnExtension = middlewareHeaders({
+  cacheControl,
+  isNamespaced: false,
+  methods: {} // will use default cache control configuration
+})

3

apps/storefront-middleware/integrations/<integration>/extensions/index.ts Re-export the newly added extension in the barrel file

export * from "./unified";
export * from "./multistore";
+export * from "./cdn";

4

Plug the extension into your integration: apps/storefront-middleware/integrations/<integration>/config.ts

- import { multistoreExtension, unifiedApiExtension } from "./extensions";
+ import { cdnExtension, multistoreExtension, unifiedApiExtension } from "./extensions";
...

  extensions: (extensions: ApiClientExtension[]) => [
    ...extensions,
    unifiedApiExtension,
+   cdnExtension,
    ...(IS_MULTISTORE_ENABLED === "true" ? [multistoreExtension] : []),
  ],

Adjusting CI/CD

The CI/CD pipeline must be adjusted to pass the git commit hash as a cache-busting ID to the Docker image of the frontend.

Without this step, the cache will not be invalidated after deploying a new version of the middleware. This may result in the new frontend version receiving API responses from the outdated middleware version, potentially causing the website to crash until the cache is invalidated or considered old.

1

Install the extension that allows for passing runtime environment variables to the frontend yarn add next-runtime-env

2

If you use github actions for your CD, ensure that you use version v3.6.0 or higher of the build-frontend action.

-        uses: vuestorefront/storefront-deployment/build-frontend@v3.4.0
+        uses: vuestorefront/storefront-deployment/build-frontend@v3.6.0

The action will automatically pass the current commit hash as a Dockerfile argument. If you don't use GitHub Actions, you need to pass NEXT_PUBLIC_CDN_CACHE_BUSTING_ID or NUXT_PUBLIC_CDN_CACHE_BUSTING_ID (depending on your framework) as a build argument to the Dockerfile during the build process. The value of this variable should be the current git commit hash of the monorepo where you develop both the storefront and the middleware.

3

We are adding a new environment variable to cache bust the old CDN cache of the middleware.

+ARG NEXT_PUBLIC_CDN_CACHE_BUSTING_ID
+ENV NEXT_PUBLIC_CDN_CACHE_BUSTING_ID=${NEXT_PUBLIC_CDN_CACHE_BUSTING_ID}

USER nextjs

ENTRYPOINT ["vue-storefront.sh"]

When the Dockerfile is executed, Docker remembers the result of each action. If the result of an action is the same as the previous run, Docker reuses the cached result. This is called Docker cache. If the Dockerfile cache is invalidated, Docker will rerun the action, increasing the build time.

Since the CDN cache-busting value changes with every commit, we don't want to excessively invalidate the Docker cache. Therefore, this value is passed just before the application startup, which is handled by the vue-storefront.sh script.

Adjusting your storefront

Enabling runtime environment variables

  1. Install next-runtime-env, this package allows NextJS to use runtime environment variables. NextJS allows only for environment variables that are bundled together with the rest of the code, but the cdn cache busting id is being set after the application is built.

yarn add next-runtime-env

  1. In apps/unified-storefront-nextjs/next.config.ts
+const { configureRuntimeEnv } = require('next-runtime-env/build/configure');

+/**
+ * The function generates the `__ENV.js` during startup file that contains the runtime environment variables
+*/
+configureRuntimeEnv();
  1. next-runtime-env works by exposing the file that contains runtime envs. The file is generated upon application startup rather than the build time. Add reference to /__ENV.js to the _document.tsx file.
...
+   <script src="/__ENV.js" /> {/* Allows for passing runtime environment variables to the browser */}
</Head>
...
  1. In apps/unified-storefront-nextjs/sdk/sdk.config.ts Add cdn cache busting id and pass the default request config to the middleware for the unified methods.
+import { env } from 'next-runtime-env';
+// the env function uses process.env while on the server side, and the __ENV.js file on the client side
+const cdnCacheBustingId = env('NEXT_PUBLIC_CDN_CACHE_BUSTING_ID') ?? 'no-cache-busting-id-set';
...
-export default defineSdkConfig(({ buildModule, middlewareModule, middlewareUrl, getRequestHeaders }) => {
+export default defineSdkConfig(({ buildModule, middlewareModule, middlewareUrl, getRequestHeaders, config }) => {
...
  unified: buildModule(middlewareModule<UnifiedEndpoints>, {
    apiUrl: `${middlewareUrl}/commerce/unified`,
+   // The CDN Cache Busting ID changes every deployment. It's used to invalidate the old Middleware cache when a new version is being deployed
+   cdnCacheBustingId: config.cdnCacheBustingId,
    defaultRequestConfig: {
      headers: getRequestHeaders(),
    },
+   methodsRequestConfig:
+     // Default methods config that configures which methods should be sent via GET requests
+     config.defaultMethodsRequestConfig.unifiedCommerce.middlewareModule
  }),

Keeping custom endpoints cacheable

If you define any new endpoints or want to customize the cache control for the existing ones, you can do it by following the steps below.

1

Define cache control for the new endpoint in the middleware apps/storefront-middleware/integrations/<integration>/extensions/cdn.ts

import middlewareHeaders from "@vsf-enterprise/middleware-headers";

const DEFAULT_MIDDLEWARE_TTL = 60 * 5;
const cacheControl = process.env.CACHE_CONTROL || `public, max-age=0, s-maxage=${DEFAULT_MIDDLEWARE_TTL}, must-revalidate`

export const cdnExtension = middlewareHeaders({
  cacheControl,
  isNamespaced: false,
  methods: {
+   myNewEcommerceEndpoint: {
+     cacheControl: `public, max-age=0, s-maxage=${60 * 15}, must-revalidate`
+   }
  }
})

2

Tell the SDK to send the request via GET method. In sdk.config.ts.

+    methodsRequestConfig:
+      // Default methods config that configures which methods should be sent via GET requests
+      config.defaultMethodsRequestConfig.unifiedCommerce.middlewareModule,
+      customMethodsRequestConfig: {
+        myNewEcommerceEndpoint: {
+          method: 'GET'
+        }
+      }
    }),

You should not cache endpoints that are mutations, respond with sensitive information, or return different data for each user.

Caching mutation endpoints would prevent mutations from being executed. Caching endpoints that respond with sensitive information can pose security risks, as cached data might be accessed by unauthorized users. Endpoints that return different data for each user should not be cached to prevent users from seeing data that belongs to someone else, which can lead to privacy violations and incorrect application behavior.

Validating if we did everything correctly

1

Run the project. If you are using Next.js, then you have to run it in a prod mode. So yarn build && yarn start from within the frontend’s directory apps/storefront-unified-nextjs. The Middleware can be executed in dev mode so yarn dev from within apps/storefront-middleware will work.

2

Open the network tab of your browser’s dev tools

3

Verify that the selected set of requests to the middleware are sent as GET requests. The Cache-Control header should be attached to the response (i.e. getProductDetails is one of the default requests that is usually cached, it is usually triggered on the product details page ). The Cache-Control header should contain the value that was set by you. If you didn't set any specific value, then the default value for s-maxage will be non-zero.