Next.jsでAPIを作ろう
このページで学ぶこと
- Next.js の Route Handlers で API エンドポイントを作れる
- GET・POST リクエストを処理できる
- フロントエンドから自分で作った API を呼び出せる
Route Handlers とは
Next.js の App Router では、app/api/ フォルダに route.js を置くことで API を作れます。
app/
├── api/
│ └── todos/
│ └── route.js → GET /api/todos, POST /api/todos
└── page.jsやってみよう
ステップ1: GET エンドポイントを作る
app/api/todos/route.js を作成し、以下のコードを書いてみましょう。
// app/api/todos/route.js
import { NextResponse } from 'next/server'
// ダミーデータ(後でDBに置き換える)
const todos = [
{ id: 1, title: '牛乳を買う', completed: false },
{ id: 2, title: '洗濯する', completed: true },
{ id: 3, title: '課題を終わらせる', completed: false },
]
// GET /api/todos されたら、`todos` をJSONで返す
export async function GET() {
return NextResponse.json(todos)
}ブラウザで http://localhost:3000/api/todos にアクセスすると、JSONが表示されます。
(ブラウザでページにアクセスする際に GET リクエストが送られるため、GET 関数が呼び出されてJSONが返される仕組みです。)
ステップ2: POST エンドポイントを追加する
app/api/todos/route.js を以下のように書き換えて、POSTリクエストも処理できるようにしてみましょう。
// app/api/todos/route.js
import { NextResponse } from 'next/server'
let todos = [
{ id: 1, title: '牛乳を買う', completed: false },
{ id: 2, title: '洗濯する', completed: true },
]
// GET /api/todos されたら、`todos` をJSONで返す
export async function GET() {
return NextResponse.json(todos)
}
// POST /api/todos
export async function POST(request) {
// リクエストボディをJSONとして読み込む
const body = await request.json()
// 簡単なバリデーション
if (!body.title) {
return NextResponse.json(
{ error: 'titleは必須です' },
{ status: 400 } // Bad Request
)
}
// 新しいTodoを作成
const newTodo = {
id: todos.length + 1,
title: body.title,
completed: false,
}
todos.push(newTodo)
return NextResponse.json(newTodo, { status: 201 }) // 新しいToDoリストと 201 Created を返す
}ステップ3: 特定IDのエンドポイントを作る
app/api/todos/[id]/route.js を作成します。
// app/api/todos/[id]/route.js
import { NextResponse } from 'next/server'
let todos = [
{ id: 1, title: '牛乳を買う', completed: false },
{ id: 2, title: '洗濯する', completed: true },
]
// GET /api/todos/:id
export async function GET(request, { params }) {
const { id } = await params // URLの [id] 部分を取得
const parsedId = parseInt(id) // idは文字列で渡されるので整数に変換
const todo = todos.find((t) => t.id === parsedId) // idに一致するTodoを探す
if (!todo) {
return NextResponse.json({ error: '見つかりません' }, { status: 404 }) // idに一致するTodoがなければ404 Not Foundを返す
}
return NextResponse.json(todo) // 見つかればそのTodoを返す
}
// DELETE /api/todos/:id
export async function DELETE(request, { params }) {
const { id } = await params // URLの [id] 部分を取得
const parsedId = parseInt(id) // idは文字列で渡されるので整数に変換
todos = todos.filter((t) => t.id !== parsedId)
return NextResponse.json({ message: '削除しました' })
}作成したら、http://localhost:3000/api/todos/1 にアクセスしてみましょう。
ToDoの内容がJSONで表示されれば成功です。
ステップ4: フロントエンドから呼び出す
app/page.js を書き換えて、自分で作った API を呼び出してみましょう。
// app/page.js
'use client'
import { useState, useEffect } from 'react'
export default function TodoApp() {
// 画面に表示するTodo一覧(APIのレスポンスを入れる)
const [todos, setTodos] = useState([])
// 入力欄の文字列
const [input, setInput] = useState('')
// 初回表示時に1回だけ GET /api/todos を呼び出して一覧を取得する
useEffect(() => {
fetch('/api/todos')
// レスポンスボディをJSONに変換
.then((res) => res.json())
// 取得した配列をstateに入れて画面に反映
.then((data) => setTodos(data))
}, [])
// 入力欄の内容で新しいTodoを作成する
const addTodo = async () => {
// 空文字の追加を防ぐ(.trim()で前後の空白は無視)
if (!input.trim()) return
// POST /api/todos にJSONを送信
const res = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: input.trim() }),
})
// 追加されたTodo(サーバーが作成したid付き)を受け取る
const newTodo = await res.json()
// 既存一覧の末尾に新しいTodoを追加して再描画
setTodos([...todos, newTodo])
// 入力欄を空に戻す
setInput('')
}
// 指定idのTodoを削除する
const deleteTodo = async (id) => {
// DELETE /api/todos/:id を呼ぶ
await fetch(`/api/todos/${id}`, {
method: 'DELETE',
})
// 削除したTodoを画面上の一覧から取り除く
setTodos(todos.filter((todo) => todo.id !== id))
}
return (
<div style={{ padding: '32px' }}>
<h1>Todoアプリ</h1>
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="新しいTodoを入力"
/>
<button onClick={addTodo}>追加</button>
</div>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.title}
{/* クリックすると該当Todoを削除 */}
<button onClick={() => deleteTodo(todo.id)} style={{ marginLeft: '8px' }}>
削除
</button>
</li>
))}
</ul>
</div>
)
}確認しよう
-
GET /api/todosでJSONが返ってくることをブラウザで確認した -
POST /api/todosでTodoを追加できた -
DELETE /api/todos/:idでTodoを削除できた - フロントエンドから
fetchでAPIを呼び出せた - 400・404 のエラーレスポンスを返せた
AIに聞いてみよう
「1から順に増やしていく方法以外にIDの付け方としてどのようなものがありますか?また、それぞれの利点と欠点はありますか?」
次のステップ
Last updated on