Next.js getStaticProps エラーの原因と解決方法【初心者向け完全ガイド】
はじめに
Next.jsは静的サイト生成(SSG)の機能を提供する人気のReactフレームワークです。その中でもgetStaticPropsは非常に重要な機能ですが、使い方を誤るとさまざまなエラーが発生します。
本記事では、getStaticPropsで発生する一般的なエラーの原因から解決方法まで、初心者でも理解できるように詳しく解説します。
getStaticPropsとは
getStaticPropsはNext.jsの特別な関数で、ビルド時にページの静的なHTMLを事前に生成するために使用されます。サーバーサイドレンダリング(SSR)とは異なり、ビルド時に一度だけ実行され、その結果がキャッシュされます。
このアプローチにより、高速なページロード、SEOの改善、サーバー負荷の軽減などのメリットが得られます。
getStaticPropsの主なエラーの原因
1. ビルド時にデータを取得できない
最も一般的なエラーの原因は、ビルド時にデータベースやAPI から正常にデータを取得できないことです。getStaticPropsはビルド時に実行されるため、その時点で外部リソースにアクセスできない場合、エラーが発生します。
2. 環境変数の不正な設定
API キーやデータベースの接続情報などの環境変数が正しく設定されていない場合、getStaticProps内でのリクエストが失敗します。
3. getStaticPropsの戻り値の型が不正
関数は必ず特定の形式のオブジェクトを返す必要があります。props、revalidate、redirectなどのプロパティは、決まった型である必要があります。
4. Dynamic Routes での getStaticPaths の欠落
動的ルーティングを使用している場合、getStaticPropsと併せてgetStaticPathsを実装する必要があります。これを忘れるとビルドエラーが発生します。
5. ページコンポーネントの外でのエクスポート忘れ
getStaticPropsは必ずページコンポーネントと同じファイルにエクスポートする必要があります。別のファイルに記述すると認識されません。
エラーの解決手順
ステップ1: エラーメッセージを確認する
まず、ビルド時のエラーメッセージを詳しく確認しましょう。Next.jsは比較的親切なエラーメッセージを出力します。エラーメッセージには、どのページで、何が原因で失敗したのかが記載されています。
ステップ2: 環境変数を確認する
.env.localファイルが正しく設定されているか確認してください。開発環境では動作していても、本番環境やCI/CDパイプラインで環境変数が設定されていない可能性があります。
ステップ3: API/データベースの接続テスト
Node.jsスクリプトを別途作成して、getStaticProps内で使用しているAPI呼び出しやデータベースクエリが正常に動作するか確認します。
ステップ4: 関数の戻り値を確認する
getStaticPropsの戻り値が正しい形式であることを確認します。TypeScriptを使用している場合は、型定義を活用するとエラーを早期に発見できます。
ステップ5: getStaticPathsの実装確認
動的ルーティングを使用している場合、getStaticPathsが正しく実装されているか確認します。
実装例とコード解説
基本的な実装例
// pages/posts/[id].js
export default function Post({ post }) {
if (!post) {
return <div>記事が見つかりません</div>;
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
export async function getStaticProps({ params }) {
try {
// APIからデータを取得
const response = await fetch(
`https://api.example.com/posts/${params.id}`
);
// エラーチェック
if (!response.ok) {
return {
notFound: true, // 404ページを表示
revalidate: 60, // 60秒後に再生成
};
}
const post = await response.json();
return {
props: {
post,
},
revalidate: 3600, // 1時間ごとに再生成
};
} catch (error) {
console.error('Error fetching post:', error);
return {
notFound: true,
revalidate: 60,
};
}
}
export async function getStaticPaths() {
try {
// 全ての記事IDを取得
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
// パスを生成
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}));
return {
paths,
fallback: 'blocking', // 新しいパスはビルド時にSSGで生成
};
} catch (error) {
console.error('Error fetching paths:', error);
return {
paths: [],
fallback: 'blocking',
};
}
}
TypeScriptでの型安全な実装
// pages/posts/[id].tsx
import type { GetStaticProps, GetStaticPaths } from 'next';
import type { ParsedUrlQuery } from 'querystring';
interface Post {
id: string;
title: string;
content: string;
}
interface Props {
post: Post;
}
interface Params extends ParsedUrlQuery {
id: string;
}
export default function Post({ post }: Props) {
if (!post) {
return <div>記事が見つかりません</div>;
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
export const getStaticProps: GetStaticProps<Props, Params> = async ({
params,
}) => {
if (!params?.id) {
return {
notFound: true,
};
}
try {
const response = await fetch(
`https://api.example.com/posts/${params.id}`
);
if (!response.ok) {
return {
notFound: true,
revalidate: 60,
};
}
const post: Post = await response.json();
return {
props: {
post,
},
revalidate: 3600,
};
} catch (error) {
console.error('Error fetching post:', error);
return {
notFound: true,
revalidate: 60,
};
}
};
export const getStaticPaths: GetStaticPaths<Params> = async () => {
try {
const response = await fetch('https://api.example.com/posts');
const posts: Post[] = await response.json();
const paths = posts.map((post) => ({
params: { id: post.id },
}));
return {
paths,
fallback: 'blocking',
};
} catch (error) {
console.error('Error fetching paths:', error);
return {
paths: [],
fallback: 'blocking',
};
}
};
環境変数を使用した実装
// pages/api-data.js
export default function Page({ data }) {
return (
<div>
<h1>データ取得例</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export async function getStaticProps() {
const apiKey = process.env.API_KEY;
const apiUrl = process.env.API_URL;
// 環境変数が設定されているか確認
if (!apiKey || !apiUrl) {
console.error('API_KEY or API_URL is not set');
return {
notFound: true,
revalidate: 60,
};
}
try {
const response = await fetch(`${apiUrl}/data`, {
headers: {
'Authorization': `Bearer ${apiKey}`,
},
});
if (!response.ok) {
throw new Error(`API returned ${response.status}`);
}
const data = await response.json();
return {
props: {
data,
},
revalidate: 3600,
};
} catch (error) {
console.error('Error in getStaticProps:', error);
return {
notFound: true,
revalidate: 60,
};
}
}
フォールバック戦略の実装
// pages/blog/[slug].js
export default function BlogPost({ post, fallback }) {
// フォールバック中はローディング表示
if (fallback) {
return <div>読み込み中...</div>;
}
if (!post) {
return <div>記事が見つかりません</div>;
}
return (
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
<div>{post.content}</div>
</article>
);
}
export async function getStaticProps({ params }) {
try {
const post = await fetchPost(params.slug);
if (!post) {
return {
notFound: true,
};
}
return {
props: {
post,
},
revalidate: 86400, // 24時間
};
} catch (error) {
console.error('Error:', error);
return {
notFound: true,
revalidate: 60,
};
}
}
export async function getStaticPaths() {
try {
// 人気のある記事のみ事前生成
const popularPosts = await fetchPopularPosts();
const paths = popularPosts.map((post) => ({
params: { slug: post.slug },
}));
return {
paths,
fallback: 'blocking', // その他のパスは必要に応じて生成
};
} catch (error) {
console.error('Error:', error);
return {
paths: [],
fallback: 'blocking',
};
}
async function fetchPost(slug) {
// 実装例
const response = await fetch(`https://api.example.com/posts/${slug}`);
if (!response.ok) return null;
return response.json();
}
async function fetchPopularPosts() {
// 実装例
const response = await fetch('https://api.example.com/popular-posts');
return response.json();
}
}
よくある間違いと対処法
間違い1: ブラウザ専用APIを使用
// ❌ 間違い
export async function getStaticProps() {
// localStorageはサーバーで実行されないため使用不可
const userId = localStorage.getItem('userId');
// ...
}
// ✅ 正解
export async function getStaticProps() {
// 環境変数やクエリパラメータを使用
const userId = process.env.DEFAULT_USER_ID;
// ...
}
間違い2: 非同期処理の完了を待たない
// ❌ 間違い
export async function getStaticProps() {
const data = fetch('https://api.example.com/data'); // awaitがない!
return {
props: { data },
};
}
// ✅ 正解
export async function getStaticProps() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return {
props: { data },
};
}
間違い3: getStaticPropsとgetServerSidePropsの混在
// ❌ 間違い(エラーが発生)
export async function getStaticProps() {
// ...
}
export async function getServerSideProps() {
// getStaticPropsと一緒に使用できない
}
// ✅ 正解(どちらか一方を使用)
export async function getStaticProps() {
// ...
}
間違い4: 動的ルートでgetStaticPathsを忘れる
// ❌ 間違い
// pages/items/[id].js
export async function getStaticProps({ params }) {
// getStaticPathsがないためビルドエラー
const item = await fetchItem(params.id);
return { props: { item } };
}
// ✅ 正解
export async function getStaticProps({ params }) {
const item = await fetchItem(params.id);
return { props: { item } };
}
export async function getStaticPaths() {
const items = await fetchAllItems();
return {
paths: items.map((item) => ({ params: { id: item.id } })),
fallback: 'blocking',
};
}
間違い5: エラーハンドリングがない
// ❌ 間違い(APIが失敗するとビルドが止まる)
export async function getStaticProps() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return { props: { data } };
}
// ✅ 正解
export async function getStaticProps() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return {
props: { data },
revalidate: 3600,
};
} catch (error) {
console.error('Error in getStaticProps:', error);
return {
notFound: true,
revalidate: 60,
};
}
}
デバッグのコツ
1. ログ出力を活用
getStaticProps内のconsole.logはビルド時に表示されます。問題の特定に活用しましょう。
export async function getStaticProps({ params }) {
console.log('Building page for ID:', params.id); // ビルド時に表示
// ...
}
2. next build で検証
開発サーバーではなく、本番ビルドコマンドnext buildを実行してエラーを確認してください。
npm run build
# または
yarn build
3. 環境変数の確認
CI/CDパイプラインで環境変数が正しく設定されているか確認します。
echo $API_URL
echo $API_KEY
実装時のベストプラクティス
1. Incremental Static Regeneration(ISR)を活用
revalidateプロパティを設定することで、定期的にページを再生成できます。これにより、データが古くなることを防げます。
2. fallback戦略を適切に選択
fallback: false:事前生成したパスのみ利用可能fallback: true:新しいパスはクライアント側で生成(SEO非推奨)fallback: 'blocking':新しいパスはサーバー側で生成(推奨)
3. エラーハンドリングを徹底
すべての外部リソースアクセスをtry-catchで包み、エラー時の処理を明確にしましょう。
4. TypeScriptの活用
TypeScriptを使用することで、型チェックにより多くのエラーをビルド時に発見できます。
まとめ
getStaticPropsは非常に強力な機能ですが、正しく使用しないと様々なエラーが発生します。本記事で紹介したポイントをまとめます。
- 環境変数の設定を確認:ビルド環境で必要な環境変数が設定されているか確認しましょう
- エラーハンドリング:API呼び出しなどの外部リソースアクセスは必ずtry-catchで包みます
- 動的ルートではgetStaticPathsを実装:動的ルーティングを使用する場合は必須です
- 戻り値の形式を確認:TypeScriptの活用により型チェックできます
- next buildで検証:開発環境での動作では不十分。本番ビルドで必ず検証します
- 適切なfallback戦略:ユースケースに応じて最適なfallback値を選択します
- ISRの活用:
revalidateを設定して定期的に再生成し、データの鮮度を保ちます
これらのポイントを押さえることで、getStaticPropsのエラーのほとんどを防ぐことができます。困ったときはビルドログを詳しく確認し、段階的にデバッグを進めることが重要です。
Next.jsの静的生成は、適切に使用すれば非常にパフォーマンスの高いWebアプリケーションを構築できます。ぜひこの記事の内容を参考にして、安定した実装を目指してください。

