Skip to content

Commit

Permalink
Merge branch 'develop' into trunk
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio committed Apr 3, 2024
2 parents bb32766 + d417993 commit 2a862d4
Show file tree
Hide file tree
Showing 30 changed files with 792 additions and 79 deletions.
6 changes: 6 additions & 0 deletions .changeset/curly-mirrors-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": patch
"@headstartwp/next": patch
---

Normalize TTL to secs
6 changes: 6 additions & 0 deletions .changeset/fast-hats-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": patch
"@headstartwp/next": patch
---

Fix: cache handler types, add isCached property
6 changes: 6 additions & 0 deletions .changeset/gentle-zoos-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": minor
"@headstartwp/next": minor
---

Introducing fetch strategy caching.
6 changes: 6 additions & 0 deletions .changeset/kind-carpets-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": patch
"@headstartwp/next": patch
---

caching: pass `fetchStrategyOptions` and `path` to cache functions.
23 changes: 23 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"mode": "pre",
"tag": "next",
"initialVersions": {
"@headstartwp/core": "1.3.4",
"@10up/react-hooks": "1.2.3",
"@headstartwp/next": "1.3.4",
"@10up/next-redis-cache-provider": "1.0.0",
"@10up/headless-docs": "1.0.0",
"@10up/wp-multisite-i18n-nextjs": "0.2.0",
"@10up/wp-multisite-nextjs": "0.2.0",
"@10up/wp-nextjs": "0.2.0",
"@10up/wp-nextjs-ts": "0.2.1",
"@headstartwp/headstartwp": "1.1.3"
},
"changesets": [
"curly-mirrors-prove",
"fast-hats-think",
"gentle-zoos-agree",
"kind-carpets-build",
"silly-bees-eat"
]
}
6 changes: 6 additions & 0 deletions .changeset/silly-bees-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": patch
"@headstartwp/next": patch
---

Make cache.enabled optional
14 changes: 13 additions & 1 deletion docs/documentation/01-Getting Started/headless-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ module.exports = {
};
```

## Splitting client/server config

You can split the config between a client and a server config. This is needed when you're setting up [cache handler](/learn/data-fetching/caching/).

Simply create a `headstartwp.config.client.js` and a `headstartwp.config.server.js` file and HeadstartWP will pick them up and inject on the appropriate bundle.

We recommend `headstartwp.config.client.js` to be the source of truth and that you import it in the server config and make only the changes needed.

## sourceUrl

The `sourceUrl` option should point to a valid WordPress installation from where the headless site should be sourced to.
Expand Down Expand Up @@ -252,4 +260,8 @@ module.exports = {
}
```

More for info check out the [preview docs](/learn/wordpress-integration/previews#the-usepostlinkforredirect-setting).
More for info check out the [preview docs](/learn/wordpress-integration/previews#the-usepostlinkforredirect-setting).

## cache

See [caching](/learn/data-fetching/caching/) docs.
180 changes: 180 additions & 0 deletions docs/documentation/02 - Data Fetching/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
slug: /data-fetching/caching
sidebar_position: 10
---

# Caching

HeadstartWP offers a way to cache the results of any fetch strategy. This can be useful when you're not rendering static pages and therefore unable to leverage ISR. However, for most cases, we encourage leveraging Next.js built-in ISR mechanism. So before considering adopting fetch strategy caching, we recommend reviewing the [on-demand revalidation docs](/learn/wordpress-integration/revalidate/).

The fetch strategy cache only runs server-side, i.e., any client-side requests coming out of fetch strategies would still reach the origin directly, this is by design since it doesn't make sense to bypass client-side fetch requests with a local cache. Additionally, certain cache handlers would simply not work in the context of the browser (redis, node-lru-cache, etc...).

On the client-side, we already cache server data via `swr` internal caching mechanism to avoid multiple requests to the origin when multiple components need the same data, anything beyond that simply doesn't make sense client-side.

You should consider fetch strategy caching if:
- You are using `getServerSideProps`
- You are creating custom fetch strategies for third-party endpoints that are slow or don't have CDN caching.
- The WordPress REST-API endpoints cannot be cached at the CDN level.

:::caution
Bear in mind that if you stack multiple layers of caching in your application it will get harder and harder to properly flush the cache. Before considering utilizing fetch strategy cache, make sure you have a plan of how content/cache will be flushed when content changes in the CMS.
:::caution


## Enabling fetch strategy caching

There are two ways to enable fetch strategy caching in HeadstartWP: individually on each `fetchHookData` call, or globally in `headstartwp.config.js`/`headstartwp.config.server.js`.

Since fetch strategy caching only runs server-side, we recommend moving the cache config to `headstartwp.config.server.js`, not doing so could lead to server-side code being injected in the client-side bundle which would break your build (i.e if you try to set up a Redis cache chandler). See [splitting config docs](/learn/getting-started/headless-config/#splitting-clientserver-config).

```js title="Enabling caching in fetchHookData"
fetchHookData(
usePost.fetcher(),
ctx,
{ cache: { enabled: true } }
);
```

`fetchHookData` cache params support the same parameters as the [cache](/api/modules/headstartwp_core/#fetchstrategycacheconfig) property of the global config.

```js title="Enabling caching in headstartwp.config.server.js"
const baseConfig = require('./headstartwp.config.client');

/**
* Config
*
* @type {import('@headstartwp/core').HeadlessConfig}
*/
module.exports = {
...baseConfig,
cache: {
enabled: ({ fetchStrategy }) => {
// cache app endpoints in-memory by default
return fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app';
},
// ttl in ms, can also be a function
ttl: 5 * 60 * 1000,
},
};
```

`cache.enabled` can also be a function so you can decide whether to cache or not based on the fetchStrategy itself.

## In-Memory caching

The default cache handler is a TTL in-memory cache. This cache handler can be useful when you're not hosting on a serverless platform (e.g. Vercel) since the node process would be alive and therefore you can cache things in memory. On serverless hosting, by design you cannot hold state between functions invocations, therefore if you try to use an In-Memory caching it would only last for the lifetime of a single request.

The main use case for In-Memory caching is when you want to cache/dedupe fetch calls when you're hosting Next.js on non-serverless hostings. Note that if you have multiple node containers/pod each one of them would have its own In-Memory caching.

## Custom cache handler

You can provide your own cache handler. For instance, if you want to cache things in Redis you can easily provide your own cache handler.

```js title="redis cache handler"
const { getRedisClient } = require('@10up/next-redis-cache-provider');
const baseConfig = require('./headstartwp.config.client');

const redisClient = getRedisClient();

/**
* Config
*
* @type {import('@headstartwp/core').HeadlessConfig}
*/
module.exports = {
...baseConfig,
cache: {
enabled: ({ fetchStrategy }) => {
// cache only the app endpoint
return fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app' || fetchStrategy.getEndpoint() === '/wp-json/wp/v2/posts';
},

ttl: ({ fetchStrategy }) => {
if (fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app') {
// 30min
return 30 * 60 * 1000;
}

// 5min
return 5 * 60 * 1000;
},

/**
* @type {import('@headstartwp/core').FetchStrategyCacheHandler}
*/
cacheHandler: {
async set(key, data, ttl) {
return redisClient.set(key, JSON.stringify(data), 'EX', ttl);
},
async get(key) {
const data = await redisClient.get(key);
return JSON.parse(data);
},
},
},
};
```

## Other options

### afterGet
This is a function that will get called after retrieving data from the cache and can be used to modify the response.

### beforeSet
This is a function that will get called before storing data in the cache and can be used to modify the data that is going to be cached. This can be used to remove things that should not be cached.

As an example, you might want to remove particular fields from the cache and always calculate it dynamically with `afterGet`.

```js title="redis cache handler with afterGet and beforeGet"
const { getRedisClient } = require('@10up/next-redis-cache-provider');
const baseConfig = require('./headstartwp.config.client');

const redisClient = getRedisClient();

/**
* Config
*
* @type {import('@headstartwp/core').HeadlessConfig}
*/
module.exports = {
...baseConfig,
cache: {
enabled: ({ fetchStrategy }) => {
// cache app endpoints in-memory by default
return fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app';
},

/**
* @type {import('@headstartwp/core').FetchStrategyCacheHandler}
*/
cacheHandler: {
async set(key, data, ttl) {
return redisClient.set(key, JSON.stringify(data), 'EX', ttl);
},
async get(key) {
const data = await redisClient.get(key);
return JSON.parse(data);
},
},

// This assumes the app endpoints include user data if user is logged in
afterGet: async ({ fetchStrategy }, data) => {
if (fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app') {
const user = await getUserData();
data.result.user = user;
}

return data;
},

beforeSet: async ({ fetchStrategy }, data) => {
if (fetchStrategy.getEndpoint() === '/wp-json/headless-wp/v1/app') {
// do not store user data
delete data.result.data;
}

return data;
},
},
};
```
Loading

0 comments on commit 2a862d4

Please sign in to comment.