0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

登録処理の実装とNext.js 13.2への移行に伴うエラーの解決方法

Posted at

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はバージョンアップに伴い、記述方法も変更されることがあります。そのため、最新のバージョンに合わせたコードの書き方を適宜適用する必要があります。バージョンアップに伴う変更点や新しいルールを把握し、コードを適切に修正することが重要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?