フォームと変更
フォームは、Webアプリケーションでデータを作成および更新するために使用します。Next.jsは、APIルートを使用してフォーム送信とデータ変更を処理する強力な方法を提供します。
補足:
- 近々、App Routerの段階的な導入と、フォーム送信とデータ変更のためのサーバーアクションの使用を推奨します。サーバーアクションにより、APIルートを手動で作成せずに、コンポーネントから直接呼び出せる非同期サーバー関数を定義できます。
- APIルートはCORSヘッダーを指定しないため、デフォルトでは同一オリジンのみです。
- APIルートはサーバー上で実行されるため、環境変数を使用して、クライアントに公開せずに機密値(APIキーなど)を使用できます。これはアプリケーションのセキュリティにとって重要です。
例
サーバー専用のフォーム
Pages Routerでは、サーバー上でデータを安全に変更するAPIエンドポイントを手動で作成する必要があります。
pages/api/submit.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const data = req.body
const id = await createItem(data)
res.status(200).json({ id })
}
次に、イベントハンドラからAPIルートを呼び出します:
pages/index.tsx
TypeScript
import { FormEvent } from 'react'
export default function Page() {
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
})
// 必要に応じてレスポンスを処理
const data = await response.json()
// ...
}
return (
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<button type="submit">送信</button>
</form>
)
}
フォームのバリデーション
基本的なクライアント側のフォームバリデーションには、required
やtype="email"
などのHTML検証を使用することをお勧めします。
より高度なサーバー側のバリデーションには、zodなどのスキーマバリデーションライブラリを使用して、データを変更する前にフォームフィールドを検証できます:
pages/api/submit.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'
const schema = z.object({
// ...
})
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const parsed = schema.parse(req.body)
// ...
}
エラー処理
フォーム送信が失敗した場合、Reactの状態を使用してエラーメッセージを表示できます:
pages/index.tsx
TypeScript
import React, { useState, FormEvent } from 'react'
export default function Page() {
const [isLoading, setIsLoading] = useState<boolean>(false)
const [error, setError] = useState<string | null>(null)
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setIsLoading(true)
setError(null) // 新しいリクエスト開始時に以前のエラーをクリア
try {
const formData = new FormData(event.currentTarget)
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
})
if (!response.ok) {
throw new Error('データの送信に失敗しました。もう一度お試しください。')
}
// 必要に応じてレスポンスを処理
const data = await response.json()
// ...
} catch (error) {
// ユーザーに表示するエラーメッセージをキャプチャ
setError(error.message)
console.error(error)
} finally {
setIsLoading(false)
}
}
return (
<div>
{error && <div style={{ color: 'red' }}>{error}</div>}
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<button type="submit" disabled={isLoading}>
{isLoading ? '読み込み中...' : '送信'}
</button>
</form>
</div>
)
}
読み込み状態の表示
フォームがサーバーに送信されている間、Reactの状態を使用して読み込み状態を表示できます:
pages/index.tsx
TypeScript
import React, { useState, FormEvent } from 'react'
export default function Page() {
const [isLoading, setIsLoading] = useState<boolean>(false)
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setIsLoading(true) // リクエスト開始時に読み込みをtrueに設定
try {
const formData = new FormData(event.currentTarget)
const response = await fetch('/api/submit', {
method: 'POST',
body: formData,
})
// 必要に応じてレスポンスを処理
const data = await response.json()
// ...
} catch (error) {
// 必要に応じてエラーを処理
console.error(error)
} finally {
setIsLoading(false) // リクエスト完了時に読み込みをfalseに設定
}
}
return (
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<button type="submit" disabled={isLoading}>
{isLoading ? '読み込み中...' : '送信'}
</button>
</form>
)
}
リダイレクト
変更後にユーザーを別のルートにリダイレクトする場合は、任意の絶対または相対URLにredirect
できます:
pages/api/submit.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const id = await addPost()
res.redirect(307, `/post/${id}`)
}
クッキーの設定
APIルート内でレスポンスのsetHeader
メソッドを使用してクッキーを設定できます:
pages/api/cookie.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
res.setHeader('Set-Cookie', 'username=lee; Path=/; HttpOnly')
res.status(200).send('クッキーが設定されました。')
}
クッキーの読み取り
APIルート内で、リクエストヘルパーのcookies
を使用してクッキーを読み取ることができます:
pages/api/cookie.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const auth = req.cookies.authorization
// ...
}
クッキーの削除
APIルート内でレスポンスのsetHeader
メソッドを使用してクッキーを削除できます:
pages/api/cookie.ts
TypeScript
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
res.setHeader('Set-Cookie', 'username=; Path=/; HttpOnly; Max-Age=0')
res.status(200).send('クッキーが削除されました。')
}