Next.js 13.2のバージョンアップに伴い、発生したエラー
今回は、page.tsxからのfetchによるPOSTリクエストをサーバー側のroute.tsで処理する際に発生したエラーについて、その解決方法を説明します。
階層図
app
├── api
│ └── register
│ └── route.ts
└── page.tsx
app/page.tsx
import { useState } from 'react';
import axios from 'axios';
export default function Register() {
const [email, setEmail] = useState('');
const [name, setName] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
setMessage('');
try {
const response = await axios.post('/api/register', { email, name });
setMessage('User registered successfully!');
} catch (error) {
if (error.response) {
setMessage(error.response.data.message);
} else {
setMessage('An unexpected error occurred');
}
}
};
return (
<div className="register-form">
<h1>Register</h1>
<form onSubmit={handleSubmit}>
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label>Name:</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<button type="submit">登録</button>
</form>
{message && <p>{message}</p>}
</div>
);
}
api/register/route.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default async function register(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
const { email, name } = req.body;
// 入力値のバリデーション
if (!email || typeof email !== 'string') {
return res.status(400).json({ message: 'Email is required and must be a string' });
}
if (name && typeof name !== 'string') {
return res.status(400).json({ message: 'Name must be a string if provided' });
}
try {
// ユーザーの作成
const user = await prisma.user.create({
data: {
email,
name,
},
});
return res.status(201).json(user);
} catch (error) {
console.error('Error creating user:', error);
if (error.code === 'P2002' && error.meta.target.includes('email')) {
return res.status(409).json({ message: 'Email already exists' });
}
return res.status(500).json({ message: 'Internal Server Error' });
}
}
・発生したエラー(登録ボタンを押したとき)
⨯ Detected default export in '/app/app/api/register/route.ts'. Export a named export for each HTTP method instead.
⨯ No HTTP methods exported in '/app/app/api/register/route.ts'. Export a named export for each HTTP method
Next.js 13.2への移行ガイドとエラー解決方法
エラー解決方法
Next.js 13.2以降では、export default async function handlerの形式ではなく、HTTPメソッドごとに名前付きエクスポートを使用する必要があります。(GET, POST, PUTなど)今回の例では、POSTを使用します。
export async function POST(req: NextRequest) {
}
その他、Next.js 13.2への移行に伴う変更点
・APIルートを設定するときのルール
Next.jsのAPIルート機能が拡張されました。APIルートはapp/apiディレクトリ内に配置され、各ファイルが独自のエンドポイントとして機能します。
・route.js(ts)ファイルは必ず、apiフォルダに配置します。
app
├── api
│ └── route.js
├── page.js
└── [user]
└── page.js
・Route Handlersでのレスポンス処理とNextResponseの使用方法
Route Handlersでは、第一引数にNextRequestのみ受け取り、レスポンスは「next/server」からimportしたNextResponseを直接使います。
Route Handlersでは、NextResponse型のオブジェクトを明示的にreturnすることで、レスポンスを返す。例示のNextResponse.json()も、NextResponse型のオブジェクトを返します。
・公式ドキュメント
https://ja.next-community-docs.dev/docs/app-router/building-your-application/routing/route-handlers
修正したソース
・訂正後
app/page.tsx
"use client"
import { useState } from 'react';
import axios from 'axios';
export default function Register() {
const [email, setEmail] = useState('');
const [name, setName] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
setMessage('');
try {
const response = await axios.post('/api/register', { email, name });
setMessage('User registered successfully!');
} catch (error) {
if (error.response) {
setMessage(error.response.data.message);
} else {
setMessage('An unexpected error occurred');
}
}
};
return (
<div className="register-form">
<h1>Register</h1>
<form onSubmit={handleSubmit}>
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label>Name:</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<button type="submit">Register</button>
</form>
{message && <p>{message}</p>}
</div>
);
}
api/register/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export async function POST(req: NextRequest) {
const { email, name } = await req.json();
// 入力値のバリデーション
if (!email || typeof email !== 'string') {
return NextResponse.json({ message: 'Email is required and must be a string' }, { status: 400 });
}
if (name && typeof name !== 'string') {
return NextResponse.json({ message: 'Name must be a string if provided' }, { status: 400 });
}
try {
// ユーザーの作成
const user = await prisma.user.create({
data: {
email,
name,
},
});
return NextResponse.json(user, { status: 201 });
} catch (error) {
console.error('Error creating user:', error);
if (error.code === 'P2002' && error.meta.target.includes('email')) {
return NextResponse.json({ message: 'Email already exists' }, { status: 409 });
}
return NextResponse.json({ message: 'Internal Server Error' }, { status: 500 });
}
}
まとめ
Next.jsはバージョンアップに伴い、記述方法も変更されることがあります。そのため、最新のバージョンに合わせたコードの書き方を適宜適用する必要があります。バージョンアップに伴う変更点や新しいルールを把握し、コードを適切に修正することが重要です。