Skip to content

Commit 9b608bb

Browse files
committed
adjust frontend cache behaviour
- server fetch is force-cache, 60s revalidation - client fetch is default behaviour which will trigger HTTP Conditional Requests
1 parent 7f559f5 commit 9b608bb

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

web/src/api/client.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,18 @@ import { deriveError } from "@/utils/error";
55
import { Options, buildRequest, buildResult } from "./common";
66

77
export const fetcher = async <T>(opts: Options): Promise<T> => {
8-
const response = await fetch(buildRequest(opts));
8+
const request = buildRequest({
9+
...opts,
10+
// We use the browser default cache behaviour for the client side requests.
11+
// There's no revalidation set on the client as we're already using SWR for
12+
// that. The default cache behaviour will however make use of browser HTTP
13+
// Conditional Requests and ETag headers which some endpoints in Storyden
14+
// provide. This results in a mostly fast experience but it's slowed down a
15+
// bit by the server side behaviour (see server.ts comment for more info.)
16+
cache: "default",
17+
});
18+
19+
const response = await fetch(request);
920

1021
return buildResult<T>(response);
1122
};

web/src/api/common.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type Options = {
1111
responseType?: string;
1212
cookie?: string;
1313
revalidate?: number;
14+
cache?: RequestCache;
1415
};
1516

1617
export class RequestError extends Error {
@@ -29,6 +30,7 @@ export function buildRequest({
2930
params,
3031
data,
3132
revalidate,
33+
cache,
3234
}: Options): Request {
3335
const apiAddress = getAPIAddress();
3436
const address = `${apiAddress}/api${url}${cleanQuery(params)}`;
@@ -42,7 +44,7 @@ export function buildRequest({
4244
credentials: "include",
4345
headers,
4446
body: buildPayload(data),
45-
cache: "default",
47+
cache,
4648
next: {
4749
tags,
4850
revalidate,

web/src/api/server.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,22 @@ export const fetcher = async <T>(url: string, opts: Options): Promise<T> => {
2222
headers,
2323
method: opts.method as any,
2424
data: opts.body,
25-
revalidate: 5,
25+
// Server side requests are cached a little more aggressively than client
26+
// side hydration requests. The downside of this is a user may see a flash
27+
// of stale data as the server render loads which will be replaced by the
28+
// client side hydration by SWR. However, the second call will most likely
29+
// be a 304 if it has been loaded before by the same user already. So, in a
30+
// best case, we get a single database read, worst case we get two. The
31+
// revalidation period is set to one minute in order to cut down on the
32+
// flashes of stale data. However, in reality this doesn't really gain much
33+
// as a user landing for the first time will still trigger two DB reads,
34+
// and a user returning is quite likely someone who has interacted with a
35+
// piece of content and thus will result in a new read at least once. So,
36+
// it's not the most efficient approach (ignoring server-side data cache)
37+
// but it's the best of a not-so-great situation. This should improve a lot
38+
// if Next.js adds support for HTTP Conditional Requests and ETag headers.
39+
revalidate: 60,
40+
cache: "force-cache",
2641
});
2742

2843
req.headers.set("Cookie", await getCookieHeader());

0 commit comments

Comments
 (0)