ブログの記事に関してそれぞれのページを用意したいと思います。前編では ID を利用して URL を作成する形で作っていきます。
ID を確認する
すでに動作しているコードを利用して、以下のようにページでの表示を切り替えます。
<h1>Content Hub ONE - Title list</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.id} - {post.title}
</li>
))}
</ul>
結果として、以下のような HTML を取得できています。
<main>
<h1>Content Hub ONE - Title list</h1>
<ul>
<li>9CKkxq4hmEiNNIeveMJQ_w<!-- --> - <!-- -->test test</li>
<li>zueShIf2qk6CzN5EL-mAIQ<!-- --> - <!-- -->Did you know Headless CMS?</li>
<li>8J0Q3r0-H02ExS1sj27v8A<!-- --> - <!-- -->Welcome to Content Hub ONE Blog</li>
</ul>
</main>
ここで取得できているのは Content Hub ONE のコンテンツに付与されている ID になります。今回はこの ID をパスに利用したいと思います。
Dynamic Routes を作成して動作確認
App Router で利用している Dynamic Routes を利用する際には、従来のファイルに id を記述する [slug].tsx のように記述するのではなく、パスとして app/[slug]/page.tsx という形でディレクトリを作成してそこにキーを設定する形になります。シンプルにサンプルをまず動かします。
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>;
}
このファイルを作成した後、http://localhost/blog/hello とアクセスすると、以下のような結果が表示されています。

まずパスの記述方法が変わっていることがわかります。この形であれば URL で ID を取得することができますが、今回はブログの ID を利用するため次の手順を追加していきます。
ID を利用して作成する
パスを作成するにあたっては、generateStaticParams という関数を利用する形となります。まず以下のコードを実行してみます。
import getAllBlog from "@/api/queries/getBlog";
export async function generateStaticParams() {
const posts = await getAllBlog();
return posts.map((post) => ({
slug: post.id,
}));
}
export default function Page({ params }: { params: { slug: string } }) {
const slug = params.slug;
return <div>{slug}</div>;
}
この段階ではまだ全ての slug を利用して設定された path のキーを返します。ここで一度 npm run build を実行するとどうなるでしょうか?以下のような形で、3つの記事を表示する動きをしているのがわかります。

ID を取得できているので、この ID を利用してブログの記事を生成していきます。
ブログの記事を取得する
ブログの記事を取得するための ID がすでにわかっているため、その ID を利用して Query を投げる形となります。
query Blog {
blog(id: "8J0Q3r0-H02ExS1sj27v8A") {
id
name
title
description
publishDate
blogImage {
results {
description
fileHeight
fileId
fileName
fileSize
fileType
fileUrl
fileWidth
id
name
}
}
}
}
この ID のところに generateStaticParams から渡された slug を利用して Query を投げたいと思います。interfaces/blog/index.ts のファイルに ID を利用してクエリを動かすコードを追加します。
export const getBlogByIdQuery = (id: string) => {
return `query Blog {
blog(id: "${id}") {
id
name
title
description
publishDate
blogImage {
results {
description
fileHeight
fileId
fileName
fileSize
fileType
fileUrl
fileWidth
id
name
}
}
}
}
`;
};
この Query をすでに作成している fetchGraphQL を利用して Slug の ID を利用してデータを取得するようにします。これに関しては、 api/queries/getBlog/index.ts に以下の関数を追加してください。
export async function getBlogBySlug(slug: string): Promise<Partial<Blog>> {
const blogResponse: BlogResponse = (await fetchGraphQL(
getBlogByIdQuery(slug)
)) as BlogResponse;
return blogResponse.data.blog;
}
以前作成をした getAllBlog は全てのブログ記事を取得しますが、今回は slug に指定された id を利用してブログの記事を返す形となります。
では app/blog/[slug]/page.tsx のファイルに戻って、Page の中を以下のように書き換えます。
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getBlogBySlug(params.slug);
if (!post) {
return notFound();
}
return <div>{post.title}</div>;
}
ここで突然 notFound(); を呼び出しています。これは next/navigation で準備されているので以下の1行を追加してください。
import { notFound } from "next/navigation";
これで ID を利用してブログのタイトルが表示されるようになります。

まとめ
ブログの個別のページに関して、ID を利用して URL を設定して表示することができました。ただ、これでは URL にキーワードも入っておらず意味がない、という感じになってしまいますので、次回はこの URL の部分をもう少し使い勝手の良い形にしたいと思います。