Skip to content

Commit

Permalink
adjust frontend cache behaviour
Browse files Browse the repository at this point in the history
- server fetch is force-cache, 60s revalidation
- client fetch is default behaviour which will trigger HTTP Conditional Requests
  • Loading branch information
Southclaws committed Jan 11, 2025
1 parent 7f559f5 commit 9b608bb
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
13 changes: 12 additions & 1 deletion web/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ import { deriveError } from "@/utils/error";
import { Options, buildRequest, buildResult } from "./common";

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

const response = await fetch(request);

return buildResult<T>(response);
};
Expand Down
4 changes: 3 additions & 1 deletion web/src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type Options = {
responseType?: string;
cookie?: string;
revalidate?: number;
cache?: RequestCache;
};

export class RequestError extends Error {
Expand All @@ -29,6 +30,7 @@ export function buildRequest({
params,
data,
revalidate,
cache,
}: Options): Request {
const apiAddress = getAPIAddress();
const address = `${apiAddress}/api${url}${cleanQuery(params)}`;
Expand All @@ -42,7 +44,7 @@ export function buildRequest({
credentials: "include",
headers,
body: buildPayload(data),
cache: "default",
cache,
next: {
tags,
revalidate,
Expand Down
17 changes: 16 additions & 1 deletion web/src/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,22 @@ export const fetcher = async <T>(url: string, opts: Options): Promise<T> => {
headers,
method: opts.method as any,
data: opts.body,
revalidate: 5,
// Server side requests are cached a little more aggressively than client
// side hydration requests. The downside of this is a user may see a flash
// of stale data as the server render loads which will be replaced by the
// client side hydration by SWR. However, the second call will most likely
// be a 304 if it has been loaded before by the same user already. So, in a
// best case, we get a single database read, worst case we get two. The
// revalidation period is set to one minute in order to cut down on the
// flashes of stale data. However, in reality this doesn't really gain much
// as a user landing for the first time will still trigger two DB reads,
// and a user returning is quite likely someone who has interacted with a
// piece of content and thus will result in a new read at least once. So,
// it's not the most efficient approach (ignoring server-side data cache)
// but it's the best of a not-so-great situation. This should improve a lot
// if Next.js adds support for HTTP Conditional Requests and ETag headers.
revalidate: 60,
cache: "force-cache",
});

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

0 comments on commit 9b608bb

Please sign in to comment.