Next.js API routes 500エラーの原因と解決方法【初心者向け完全ガイド】
Next.jsでAPIルートを使用している際に、突然500エラーが発生したことはありませんか?500エラーはサーバー側のエラーを示す最も一般的なHTTPステータスコードで、原因を特定するのが難しいことで知られています。本記事では、Next.js API routesで発生する500エラーの原因から解決方法まで、初心者でもわかりやすく説明します。
Next.js API routes 500エラーとは
500エラーはHTTP 500 Internal Server Errorの略で、サーバー側で予期しないエラーが発生したことを示します。Next.jsのAPIルートで500エラーが返される場合、サーバー実行時に何らかの例外が発生していることを意味します。クライアント側(ブラウザ)からは「何か悪いことが起こった」という情報しか得られないため、デバッグが困難になるケースが多いです。
Next.js API routes 500エラーの主な原因
1. 未処理の例外やエラー
APIルート内でエラーが発生し、それが適切にキャッチされていない場合、500エラーが返されます。例えば、データベース接続エラーやネットワークエラーが発生した際に、try-catchで処理されていないと500エラーになります。
2. 環境変数の不足や誤設定
APIルート内で環境変数を参照していますが、本番環境で環境変数が正しく設定されていない場合、エラーが発生します。特にデータベースのURLやAPIキーなどがundefinedになっていることが原因のケースが多いです。
3. データベース接続エラー
MongoDBやPostgreSQLなどのデータベースに接続できない場合、500エラーが発生します。接続文字列の誤りや、ネットワークの問題が原因となることが一般的です。
4. メモリリークやリソース枯渇
APIルートが無限ループに入ったり、大量のメモリを消費したりすると、Node.jsプロセスがクラッシュし500エラーが返されます。
5. 非同期処理の処理ミス
async/awaitの使い方が誤っていたり、Promiseが正しく返されていない場合、予期しない動作が発生します。
6. JSONレスポンスの形式エラー
res.json()に循環参照を含むオブジェクトを渡したり、BigIntなど直列化できない値を含めたりすると、500エラーが発生することがあります。
500エラーの解決手順
ステップ1: コンソールログで詳細を確認する
まずはサーバー側のコンソールで詳細なエラーメッセージを確認しましょう。ローカル開発環境でnpm run devを実行している場合、ターミナルにエラースタックが出力されます。本番環境ではログサービスを活用してエラーを確認します。
ステップ2: try-catchを使用してエラーハンドリングを実装する
全てのAPIルートに適切なエラーハンドリングを追加してください。これにより、エラーの詳細をログに出力したり、クライアントに有用な情報を返したりできます。
ステップ3: 環境変数を確認する
環境変数が正しく設定されているか、.env.localと本番環境の設定を比較してください。特にAPIキーやデータベース接続文字列は慎重に確認が必要です。
ステップ4: ネットワークと接続性をテストする
APIルートが外部サービスに接続する場合、その接続がサーバーから可能かテストしてください。ファイアウォールやセキュリティグループの設定を確認しましょう。
ステップ5: デバッグモードで詳細をログ出力する
本番環境ではログサービス(例:Sentry、LogRocket)を導入し、500エラーが発生した際の詳細情報を記録してください。
解決策:コード例
基本的なエラーハンドリングの実装
// pages/api/users.js
export default async function handler(req, res) {
try {
// リクエストメソッドの確認
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method not allowed' });
}
// 環境変数の確認
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not defined');
}
// データベース接続やAPI呼び出し
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 res.status(200).json({ success: true, data });
} catch (error) {
// エラーログを出力
console.error('API error:', error);
// クライアントにエラーメッセージを返す
return res.status(500).json({
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
}
データベース接続エラーの解決例
// pages/api/products.js
import { MongoClient } from 'mongodb';
export default async function handler(req, res) {
const client = new MongoClient(process.env.MONGODB_URI);
try {
// 接続タイムアウトを設定
await client.connect({ serverSelectionTimeoutMS: 5000 });
const db = client.db('myapp');
const products = await db.collection('products').find({}).toArray();
res.status(200).json({ success: true, products });
} catch (error) {
console.error('Database connection error:', error);
// 接続エラーの種類に応じた処理
if (error.name === 'MongoServerSelectionError') {
return res.status(503).json({
error: 'Database connection failed',
details: 'Service temporarily unavailable'
});
}
res.status(500).json({ error: 'Internal server error' });
} finally {
await client.close();
}
}
非同期処理の正しい実装例
// pages/api/async-data.js
export default async function handler(req, res) {
try {
// 複数の非同期処理を並列実行
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
res.status(200).json({ users, posts });
} catch (error) {
console.error('Error fetching data:', error);
res.status(500).json({ error: 'Failed to fetch data' });
}
}
// ヘルパー関数
async function fetchUsers() {
const response = await fetch('https://api.example.com/users');
if (!response.ok) throw new Error('Failed to fetch users');
return response.json();
}
async function fetchPosts() {
const response = await fetch('https://api.example.com/posts');
if (!response.ok) throw new Error('Failed to fetch posts');
return response.json();
}
環境変数の検証
// lib/validateEnv.js
export function validateRequiredEnvVars(requiredVars) {
const missing = requiredVars.filter(
variable => !process.env[variable]
);
if (missing.length > 0) {
throw new Error(
`Missing required environment variables: ${missing.join(', ')}`
);
}
}
// pages/api/secure-endpoint.js
import { validateRequiredEnvVars } from '@/lib/validateEnv';
export default async function handler(req, res) {
try {
validateRequiredEnvVars(['DATABASE_URL', 'API_KEY', 'SECRET']);
// APIの処理
res.status(200).json({ message: 'Success' });
} catch (error) {
console.error('Configuration error:', error);
res.status(500).json({ error: error.message });
}
}
カスタムエラーハンドリング関数
// lib/apiHandler.js
export function createApiHandler(handler) {
return async (req, res) => {
try {
return await handler(req, res);
} catch (error) {
console.error('Unhandled API error:', {
timestamp: new Date().toISOString(),
path: req.url,
method: req.method,
error: error.message,
stack: error.stack
});
// エラータイプに応じた処理
if (error.statusCode) {
return res.status(error.statusCode).json({
error: error.message
});
}
res.status(500).json({
error: 'Internal server error'
});
}
};
}
// 使用例
// pages/api/example.js
import { createApiHandler } from '@/lib/apiHandler';
const handler = createApiHandler(async (req, res) => {
const data = await fetchData();
res.status(200).json(data);
});
export default handler;
よくある間違いと対策
間違い1: エラーハンドリングがない
間違った例:
export default async function handler(req, res) {
const data = await fetch('https://api.example.com/data');
const json = await data.json();
res.json(json); // エラーが発生してもキャッチされない
}
正しい例:
export default async function handler(req, res) {
try {
const data = await fetch('https://api.example.com/data');
if (!data.ok) throw new Error('API failed');
const json = await data.json();
res.json(json);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Failed to fetch data' });
}
}
間違い2: 環境変数をチェックしない
間違った例:
const apiKey = process.env.API_KEY; // undefinedの可能性
const response = await fetch(`https://api.example.com?key=${apiKey}`);
正しい例:
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('API_KEY environment variable is not set');
}
const response = await fetch(`https://api.example.com?key=${apiKey}`);
間違い3: Promiseを待たない
間違った例:
export default async function handler(req, res) {
fetchData(); // awaitがない
res.json({ message: 'ok' }); // データが取得される前にレスポンスを返す
}
正しい例:
export default async function handler(req, res) {
const data = await fetchData(); // awaitを忘れない
res.json({ message: 'ok', data });
}
間違い4: JSONに直列化できない値を含める
間違った例:
const date = new Date();
const bigNumber = BigInt(9007199254740991);
res.json({ date, bigNumber }); // エラーが発生
正しい例:
const date = new Date();
const bigNumber = BigInt(9007199254740991);
res.json({
date: date.toISOString(),
bigNumber: bigNumber.toString()
});
間違い5: レスポンスを複数回送信する
間違った例:
export default async function handler(req, res) {
res.json({ message: 'first' });
res.json({ message: 'second' }); // エラーが発生
}
正しい例:
export default async function handler(req, res) {
if (someCondition) {
return res.json({ message: 'first' }); // returnを使う
}
return res.json({ message: 'second' });
}
デバッグのテクニック
1. コンソールログの活用
export default async function handler(req, res) {
console.log('Received request:', { method: req.method, url: req.url });
try {
console.log('Environment check:', {
hasDbUrl: !!process.env.DATABASE_URL
});
const data = await fetchData();
console.log('Data fetched successfully:', data);
res.status(200).json(data);
} catch (error) {
console.error('Full error object:', {
name: error.name,
message: error.message,
code: error.code,
stack: error.stack
});
res.status(500).json({ error: error.message });
}
}
2. ローカルテストの重要性
本番環境で発生した500エラーは、まずローカル環境で再現させることが重要です。本番環境の環境変数をコピーして、ローカル.env.localに設定し、同じ条件でテストしましょう。
3. Postmanやcurlでのテスト
# cURLでAPIをテスト
curl -X GET http://localhost:3000/api/users \
-H \"Content-Type: application/json\" \
-v
# POSTリクエストのテスト
curl -X POST http://localhost:3000/api/users \
-H \"Content-Type: application/json\" \
-d '{\"name\": \"John\", \"email\": \"john@example.com\"}' \
-v
本番環境でのエラーモニタリング
Sentryを使用したエラートラッキング
// pages/api/example.js
import * as Sentry from \"@sentry/nextjs\";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
});
export default async function handler(req, res) {
try {
const data = await fetchData();
res.status(200).json(data);
} catch (error) {
// Sentryに自動的に送信される
Sentry.captureException(error);
res.status(500).json({ error: 'Internal server error' });
}
}
まとめ
Next.js API routesで500エラーが発生した場合、以下のポイントに注意して対応することが重要です:
- 適切なエラーハンドリング:全てのAPIルートにtry-catchを実装し、エラーを適切に処理する
- 環境変数の検証:APIキーやデータベース接続文字列が正しく設定されているか確認する
- 非同期処理の正確性:async/awaitを正しく使用し、Promiseが適切に解決されるまで待つ
- 詳細なログ出力:本番環境ではSentryなどのロギングサービスを導入し、エラーの詳細を記録する
- ローカルでのテスト:本番環境で発生したエラーは、まずローカル環境で再現させてから解決する
これらの対策を実施することで、500エラーの大多数は予防・解決することができます。初心者でも、この記事で紹介したコード例を参考にすれば、Next.jsアプリケーションのエラーハンドリングを大幅に改善できるでしょう。

