こんにちは。
個人開発で作成しているmicroCMSとNext.jsを利用したブログサイトにおいて、Next.jsの404ページ表示用コンポーネントであるnot-found.js(not-found.tsx)を記載したにも関わらず404ページが表示できずにエラーになる事象が起きました。
その事象・原因および解決法について備忘録代わりに記載いたします。
発生したエラー
Next.jsの公式ドキュメント通り、not-found.tsxをappフォルダ直下に以下のように配置しました。
// コードはNext.js公式ドキュメントのものを転載
import Link from 'next/link'
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
</div>
)
}
またnot-found.tsx作成時のフォルダ構成は以下のような形でした。
app
- page.tsx
- layout.tsx
- not-found.tsx
- [id]
- page.tsx
この状態でlocalhost:3000/hogeのように存在しないフォルダやidのURLを記載したところ、404ページではなく500エラーとなり、以下のエラーが発生しました。
fetch API response status: 404
原因と対策
結論としては、原因はpage.tsxの例外処理として404処理を呼び出す関数を記載していないことでした。
// microcms-js-sdkを使用。ドメインとAPIキーを取得し、client定数に登録
import { createClient } from 'microcms-js-sdk';
if (!process.env.MICROCMS_SERVICE_DOMAIN) {
throw new Error('MICROCMS_SERVICE_DOMAIN is required');
}
if (!process.env.MICROCMS_API_KEY) {
throw new Error('MICROCMS_API_KEY is required');
}
export const client = createClient({
serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
apiKey: process.env.MICROCMS_API_KEY,
});
import Link from 'next/link';
import { client } from '../../libs/client'
export default async function Page({ params }) {
const { id } = await params;
async function getBlogDetail() {
const data = await client.get({
endpoint: 'blogs',
contentId: id,
});
return data;
}
const contents = await getBlogDetail();
return (
<>
// ここにブログ記事の内容を出力するためのコードが入ります
</>
)
}
Next.jsのドキュメントにもある通り、本来はnotfound用の関数は別途スローさせる必要がありました。
not-foundファイルは、ルートセグメント内でnotFound関数がスローされた場合にUIを描画するために使用されます。カスタムUIを提供するだけでなく、Next.jsはストリーミングレスポンスの場合は200のHTTPステータスコードを、非ストリーミングレスポンスの場合は404を返します。
引用元:https://nextjsjp.org/docs/app/api-reference/file-conventions/not-found
※太字は筆者
[id]/page.tsxのページパラメータの取得関数には例外処理が含まれていなかったため、404へのルートではなく500エラーが発生していたということでした。
対策
page.tsxのブログ情報取得関数に例外処理をつけ、例外処理の中でnotFound関数を返すことで404ページを意図通りに出力できました。
import Link from 'next/link';
import { client } from '../../libs/client'
+ import { notFound } from 'next/navigation';
export default async function Page({ params }) {
const { id } = await params;
async function getBlogDetail() {
// try...catch...節を追加
try {
const data = await client.get({
endpoint: 'blogs',
contentId: id,
});
return data;
} catch {
notFound();
}
}
const contents = await getBlogDetail();
return (
<>
// ここにブログ記事の内容を出力するためのコードが入ります
</>
)
}
最後に
ここまで読んでいただきありがとうございます。
エラーハンドリング系の関数は、単にファイルを追加するだけでなく関数が果たす役割・エラー処理の全体像まで掴んで実装することが重要とわかりました。
参考資料