Effective use of caching in the browser and at the edge is essential to providing the fastest experience for your users. React Storefront allows you to control both client and edge caching from a single place: your router definition.
This guide gives you everything you need to know to cache responses effectively using React Storefront.
You can make any route cacheable by adding a cache
handler to the route.
For example:
// src/routes.js
import { Router, fromServer, cache } from 'react-storefront/router'
new Router()
.get('/',
cache({ client: true, edge: { maxAgeSeconds: 300 }}) // cache in the service-worker and at the edge for up to 5 minutes
fromServer('./home/home-handler')
)
The cache handler takes an object with with two properties:
true
to cache responses on the client using the service worker. Set to false
or omit to prevent caching on the client.You can also set maxAgeSeconds
in a fromServer
handler by calling response.cacheAtEdge({ maxAgeSeconds: (time) })
. Here's an example:
function myHandler(params, request, response) {
response.cacheAtEdge({ maxAgeSeconds: time })
return {
title: 'Hello World'
}
}
All caching on the client is managed by the service worker that react-storefront automatically generates when your app is built.
In additon to specifying cache({ client: true })
on each route, you can configure client-side caching parameters for all routes using router.configureClientCache(options)
:
// src/routes.js
import { Router } from 'react-storefront/router'
new Router().configureClientCache({
maxEntries: 1000,
maxAgeSeconds: 300
})
The configureClientCache
function takes an object with the following properties:
Property | Default | Description |
---|---|---|
maxEntries | 200 | The maximum number of entries stored in the browser's cache |
maxAgeSeconds | 3600 | The time to live of each entry |
When you specify a cache handler with, for example, { edge: { maxAgeSeconds: 300 }}
, React storefront adds a cache-control: no-cache, s-maxage: 300
header to the response. This instructs the hosting platform to cache the response for the configured number of seconds.
The default cache key in Moovweb XDN consists of the following request parameters:
Implications are as following:
You can configure the Moovweb XDN to store multiple variants for any given route by specifying a custom cache key. This is essential when you need to serve multiple languages, currencies, or shipping locations from the same domain.
Here is an example which uses a cookie called "currency" to split the cache:
import {
Router,
fromClient,
fromServer,
cache,
createCustomCacheKey
} from 'react-storefront/router'
new Router().get(
'/p/:id',
cache({
edge: {
maxAgeSeconds: 3600,
key: createCustomCacheKey().addCookie('currency') // split the cache based on the currency cookie
}
}),
fromClient({ page: 'Product' }),
fromServer('./product/product-handler')
)
In this scenario, the Moovweb XDN will store a separate response in the cache for each value of the currency cookie. For example, A user with a currency cookie of "USD" will get a different cached response from a user with a currency cookie of "EUR".
You can return a single cached response for multiple values of a cookie by passing an optional partitioning callback to addCookie()
. Here we create two partitions: one called "na" representing North America, consisting of "us" and "ca", and one for all others.
In this example users with a location cookie with value of "us" or "ca" will share one response, while all other users will share a different response:
createCustomCacheKey().addCookie('location', cookie => {
cookie.partition('na').byPattern('^(us|ca)$')
cookie.partition('others').byPattern('.*')
})
If you're using cookies to cache multiple variants of a page at the edge, and those cookies can be set by the user (for example, by allowing the user to change their currency preference), it is important to clear the client cache when those cookies change so that stale responses are discarded. This can be done using clearClientCache
from react-storefront/cache
:
import React, { useState } from 'react'
import { clearClientCache } from 'react-storefront/cache'
function CurrencySelection() {
const [value, setValue] = useState('USD')
const onChange = e => {
// update the cookie
document.cookie = `currency=${e.target.value}`
// since we're splitting the cache by currency, we need to clear the client cache
clearClientCache()
// update local component state
setValue(e.target.value)
}
return (
<select onChange={onChange} value={value}>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
</select>
)
}
You can also split the cache by request headers and optionally group values by pattern:
new Router().get(
'/p/:id',
cache({
edge: {
maxAgeSeconds: 3600,
key: createCustomCacheKey()
.addHeader('x-moov-xdn-device-is-bot')
.addHeader('x-moov-xdn-geo-country-code', header => {
header.partition('na').byPattern('^(us|ca)$')
header.partition('others').byPattern('.*')
}) // split the cache based on the location header
}
}),
fromClient({ page: 'Product' }),
fromServer('./product/product-handler')
)
URLs often contain unique tracking codes in order to track customer engagement. A common example is embedding a unique user id in links that are sent out as part of an email ad campaign. Consider the following URL:
https://mystore.com/products/red-shirt?uid=1234567
Here the uid
query parameter is used to track that the user has clicked on the link. Because the value of this query parameter is different for every user, it will never be served from the cache. Therefore, the initial landing on the site will be quite slow for all users. By configuring your routes to ignore the uid
query parameter, we can serve this page to users from the cache:
new Router().get(
'/p/:id',
cache({
edge: {
maxAgeSeconds: 3600,
key: createCustomCacheKey().excludeQueryParameters('uid') // ignore the uid query paramater
}
}),
fromClient({ page: 'Product' }),
fromServer('./product/product-handler')
)
You can also ignore all query parameters:
createCustomCacheKey().excludeAllQueryParameters() // ignore all query parameters
Or, exclude all but specific query parameters:
createCustomCacheKey().excludeAllQueryParametersExcept('search', 'style') // ignore all query parameters except "search" and "style"
Note that when you exclude unique tracking query parameters from the cache key, you need to report the user interaction via JavaScript running in the browser, after the app mounts, not on the server.
React Storefront allows you to assign one or more surrogate keys to each route. A surrogate is a value that you can use to clear a group of responses from the cache using the Moovweb Control Center.
Here's an example of how you can assign the same surrogate key to multiple routes:
new Router()
.get(
'/categories/:id',
cache({
edge: {
surrogateKey: (params, request) => {
return 'product-catalog'
}
}
})
)
.get(
'/products/:id',
cache({
edge: {
surrogateKey: (params, request) => {
return 'product-catalog'
}
}
})
)
Here both category and product routes are assigned the 'product-catalog' surrogate key. Assigning this surrogate key would allow you to clear only these two routes from the cache while leaving the rest of the cached pages intact using the Moovweb Control Center:
You can also clear items with a particular surrogate key from the cache from the command line using moovsdk
:
$ moovsdk cacheclear --surrogate-key product-catalog
All routes are automatically given the handler path as a surrogate key. So for example, if you have:
new Router().get(
'/products/:id',
cache({ edge: { maxAgeSeconds: 1000 } }),
fromClient({ page: 'Product' }),
fromServer('./products/product-handler')
)
You can remove all product pages from the cache using the Moovweb Control Center...
... or moovsdk ...
$ moovsdk cacheclear --surrogate-key ./products/product-handler
You can assign multiple surrogate keys to a response by separating them with a space:
.get(
'/products/:id',
cache({
edge: {
surrogateKey: (params, request) => {
return `all-products product-${params.id}`
}
}
})
)
In this example, you could clear a URL of /products/1
from the cache using:
$ moovsdk cacheclear --surrogate-key product-1
Or you could clear all products using:
$ moovsdk cacheclear --surrogate-key all-products
In order to prevent responses with set-cookie
headers from being cache (and thus possibly leaking user-specific information like a session id to multiple users), React Storefront automatically removes all set-cookie headers when your route is configured to cache at the edge.
Your JS bundles are far-future cached and contain a cache-busting hash in the filename. Therefore they are safe to cache in the browser (even when service workers are not supported) and on the downstream CDN.
All other static assets will not have a cache-control
header. They will be cached based on the CDN and browsers default behavior. If hosting on the Moovweb XDN, all static assets are far-future cached at the edge.
Moovweb's XDN platform is automatically configured to respect this header. If you're hosting on another platform, you'll probably need to configure it to do so.
/
as the path as shown in the image below:
Note that specifying a path of /
will clear the cache for when the homepage is requested with a trailing /
(e.g. https://www.site.com/
) and also for when the homepage is requested without a trailing /
(e.g. https://www.site.com
).
You can use the x-moov-t response header. If you see vc=hit
in the x-moov-t
response header. A value of vc=cached
indicates a miss but the result has been cached for the next request. All other values indicate a cache miss.
There is also a x-moov-cache-reason
that specifies whether or not the result was cached and why. For example:
x-moov-cache-reason: cached: cache-control: s-maxage found in response
If you see a cache-control
response header with an s-maxage
value, Moov XDN will cache it.
s-maxage
value when my app is deployed on Moov XDN?Moov XDN strips this value off before it sends the response. This helps ensure that the downstream CDN won't cache the response. This is important to prevent stale responses that would otherwise occur after you deploy an update to your application.
Moov XDN automatically clears the edge cache when you deploy an update to your application.
You can also clear it manually at any time using the Moovweb Control Center. Navigate to your project, then select Project Settings => Project Admin => Clear Hosts Cache.
You will not receive a cached response.