Skip to content

Commit 8ba89a9

Browse files
committed
1 parent 2cfa261 commit 8ba89a9

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
2+
export const dynamicParams = false; // or true, to make it try SSR unknown paths
3+
4+
const POSTS = Array.from({ length: 20 }, (_, i) => ({
5+
id: String(i + 1),
6+
title: `Post ${i + 1}`,
7+
content: `This is post ${i + 1}`,
8+
}));
9+
10+
async function fakeGetPostsFetch() {
11+
return POSTS.slice(0, 10);
12+
}
13+
14+
async function fakeGetPostFetch(id: string) {
15+
return POSTS.find((post) => post.id === id);
16+
}
17+
18+
export async function generateStaticParams() {
19+
const fakePosts = await fakeGetPostsFetch();
20+
return fakePosts.map((post) => ({
21+
id: post.id,
22+
}));
23+
}
24+
25+
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
26+
const { id } = await params;
27+
const post = await fakeGetPostFetch(id);
28+
return (
29+
<main>
30+
<h1 data-testid="title">{post?.title}</h1>
31+
<p data-testid="content">{post?.content}</p>
32+
</main>
33+
);
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { notFound } from "next/navigation";
2+
3+
// We'll prerender only the params from `generateStaticParams` at build time.
4+
// If a request comes in for a path that hasn't been generated,
5+
// Next.js will server-render the page on-demand.
6+
// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
7+
export const dynamicParams = true; // or false, to 404 on unknown paths
8+
9+
const POSTS = Array.from({ length: 20 }, (_, i) => ({
10+
id: String(i + 1),
11+
title: `Post ${i + 1}`,
12+
content: `This is post ${i + 1}`,
13+
}));
14+
15+
async function fakeGetPostsFetch() {
16+
return POSTS.slice(0, 10);
17+
}
18+
19+
async function fakeGetPostFetch(id: string) {
20+
return POSTS.find((post) => post.id === id);
21+
}
22+
23+
export async function generateStaticParams() {
24+
const fakePosts = await fakeGetPostsFetch();
25+
return fakePosts.map((post) => ({
26+
id: post.id,
27+
}));
28+
}
29+
30+
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
31+
const { id } = await params;
32+
const post = await fakeGetPostFetch(id);
33+
if (Number(id) === 1337) {
34+
throw new Error("This is an error!");
35+
}
36+
if (!post) {
37+
notFound();
38+
}
39+
return (
40+
<main>
41+
<h1 data-testid="title">{post.title}</h1>
42+
<p data-testid="content">{post.content}</p>
43+
</main>
44+
);
45+
}

examples/e2e/app-router/e2e/isr.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,58 @@ test("Incremental Static Regeneration with data cache", async ({ page }) => {
9898
expect(originalCachedDate).toEqual(finalCachedDate);
9999
expect(originalFetchedDate).toEqual(finalFetchedDate);
100100
});
101+
102+
test.describe("dynamicParams set to true", () => {
103+
test("should be HIT on a path that was prebuilt", async ({ page }) => {
104+
const res = await page.goto("/isr/dynamic-params-true/1");
105+
expect(res?.status()).toEqual(200);
106+
expect(res?.headers()["x-nextjs-cache"]).toEqual("HIT");
107+
const title = await page.getByTestId("title").textContent();
108+
const content = await page.getByTestId("content").textContent();
109+
expect(title).toEqual("Post 1");
110+
expect(content).toEqual("This is post 1");
111+
});
112+
113+
// In `next start` this test would fail on subsequent requests because `x-nextjs-cache` would be `HIT`
114+
// However, once deployed to AWS, Cloudfront will cache `MISS`
115+
test("should SSR on a path that was not prebuilt", async ({ page }) => {
116+
const res = await page.goto("/isr/dynamic-params-true/11");
117+
expect(res?.headers()["x-nextjs-cache"]).toEqual("MISS");
118+
const title = await page.getByTestId("title").textContent();
119+
const content = await page.getByTestId("content").textContent();
120+
expect(title).toEqual("Post 11");
121+
expect(content).toEqual("This is post 11");
122+
});
123+
124+
test("should 404 when you call notFound", async ({ page }) => {
125+
const res = await page.goto("/isr/dynamic-params-true/21");
126+
expect(res?.status()).toEqual(404);
127+
expect(res?.headers()["cache-control"]).toBe("private, no-cache, no-store, max-age=0, must-revalidate");
128+
await expect(page.getByText("404")).toBeAttached();
129+
});
130+
131+
test("should 500 for a path that throws an error", async ({ page }) => {
132+
const res = await page.goto("/isr/dynamic-params-true/1337");
133+
expect(res?.status()).toEqual(500);
134+
expect(res?.headers()["cache-control"]).toBe("private, no-cache, no-store, max-age=0, must-revalidate");
135+
});
136+
});
137+
138+
test.describe("dynamicParams set to false", () => {
139+
test("should be HIT on a path that was prebuilt", async ({ page }) => {
140+
const res = await page.goto("/isr/dynamic-params-false/1");
141+
expect(res?.status()).toEqual(200);
142+
expect(res?.headers()["x-nextjs-cache"]).toEqual("HIT");
143+
const title = await page.getByTestId("title").textContent();
144+
const content = await page.getByTestId("content").textContent();
145+
expect(title).toEqual("Post 1");
146+
expect(content).toEqual("This is post 1");
147+
});
148+
149+
test("should 404 for a path that is not found", async ({ page }) => {
150+
const res = await page.goto("/isr/dynamic-params-false/11");
151+
expect(res?.status()).toEqual(404);
152+
expect(res?.headers()["cache-control"]).toBe("private, no-cache, no-store, max-age=0, must-revalidate");
153+
await expect(page.getByText("404")).toBeAttached();
154+
});
155+
});

0 commit comments

Comments
 (0)