diff --git a/.changeset/sour-points-bake.md b/.changeset/sour-points-bake.md new file mode 100644 index 000000000..11f8167f1 --- /dev/null +++ b/.changeset/sour-points-bake.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +fix fetch cache for next 15 diff --git a/examples/app-router/app/ssr/page.tsx b/examples/app-router/app/ssr/page.tsx index 4f57336df..e41137576 100644 --- a/examples/app-router/app/ssr/page.tsx +++ b/examples/app-router/app/ssr/page.tsx @@ -14,10 +14,14 @@ async function getTime() { export default async function SSR() { const time = await getTime(); const headerList = await headers(); + const responseOpenNext = await fetch("https://opennext.js.org", { + cache: "force-cache", + }); return (

Time: {time}

{headerList.get("host")}
+

Cached fetch: {responseOpenNext.headers.get("date")}

); } diff --git a/packages/open-next/src/adapters/cache.ts b/packages/open-next/src/adapters/cache.ts index 82c4e1bcf..b357d0dbc 100644 --- a/packages/open-next/src/adapters/cache.ts +++ b/packages/open-next/src/adapters/cache.ts @@ -108,6 +108,28 @@ declare global { var lastModified: Record; var isNextAfter15: boolean; } + +function isFetchCache( + options?: + | boolean + | { + fetchCache?: boolean; + kindHint?: "app" | "pages" | "fetch"; + kind?: "FETCH"; + }, +): boolean { + if (typeof options === "boolean") { + return options; + } + if (typeof options === "object") { + return ( + options.kindHint === "fetch" || + options.fetchCache || + options.kind === "FETCH" + ); + } + return false; +} // We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time export default class S3Cache { constructor(_ctx: CacheHandlerContext) {} @@ -122,21 +144,16 @@ export default class S3Cache { kindHint?: "app" | "pages" | "fetch"; tags?: string[]; softTags?: string[]; + kind?: "FETCH"; }, ) { if (globalThis.disableIncrementalCache) { return null; } - const isFetchCache = - typeof options === "object" - ? options.kindHint - ? options.kindHint === "fetch" - : options.fetchCache - : options; const softTags = typeof options === "object" ? options.softTags : []; const tags = typeof options === "object" ? options.tags : []; - return isFetchCache + return isFetchCache(options) ? this.getFetchCache(key, softTags, tags) : this.getIncrementalCache(key); } diff --git a/packages/tests-e2e/tests/appRouter/ssr.test.ts b/packages/tests-e2e/tests/appRouter/ssr.test.ts index cd472c44f..261189115 100644 --- a/packages/tests-e2e/tests/appRouter/ssr.test.ts +++ b/packages/tests-e2e/tests/appRouter/ssr.test.ts @@ -53,3 +53,12 @@ test("Server Side Render and loading.tsx", async ({ page }) => { // await expect(el).toBeVisible(); // await expect(time).not.toEqual(newTime); }); + +test("Fetch cache properly cached", async ({ page }) => { + await page.goto("/ssr"); + const originalDate = await page.getByText("Cached fetch:").textContent(); + await wait(2000); + await page.reload(); + const newDate = await page.getByText("Cached fetch:").textContent(); + expect(originalDate).toEqual(newDate); +});