Next.js 14とServer Actionsでフルスタックアプリを構築
Next.js 14では安定版のServer Actionsが導入され、Reactコンポーネント内で直接サーバーサイドロジックを記述できるようになりました。APIルートは不要です。このチュートリアルでは、タスク管理アプリをゼロから構築します。
前提条件
- Node.js 18以上
- ReactとTypeScriptの基本知識
1. プロジェクトセットアップ
npx create-next-app@latest task-app --typescript --tailwind --app
cd task-app
npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite
2. データベーススキーマの定義
prisma/schema.prismaを編集:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
model Task {
id Int @id @default(autoincrement())
title String
completed Boolean @default(false)
createdAt DateTime @default(now())
}
マイグレーションを実行:
npx prisma migrate dev --name init
3. Prismaクライアントの作成
lib/prisma.tsを作成:
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
4. Server Actionsの構築
app/actions.tsを作成:
'use server'
import { prisma } from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
export async function addTask(formData: FormData) {
const title = formData.get('title') as string
if (!title?.trim()) return
await prisma.task.create({ data: { title: title.trim() } })
revalidatePath('/')
}
export async function toggleTask(id: number) {
const task = await prisma.task.findUnique({ where: { id } })
if (!task) return
await prisma.task.update({
where: { id },
data: { completed: !task.completed },
})
revalidatePath('/')
}
export async function deleteTask(id: number) {
await prisma.task.delete({ where: { id } })
revalidatePath('/')
}
5. UIの構築
app/page.tsxを置き換え:
import { prisma } from '@/lib/prisma'
import { addTask, toggleTask, deleteTask } from './actions'
export default async function Home() {
const tasks = await prisma.task.findMany({
orderBy: { createdAt: 'desc' },
})
return (
<main className="max-w-xl mx-auto p-8">
<h1 className="text-3xl font-bold mb-6">タスクマネージャー</h1>
<form action={addTask} className="flex gap-2 mb-8">
<input
name="title"
placeholder="新しいタスクを追加..."
className="flex-1 border rounded px-3 py-2"
required
/>
<button
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
>
追加
</button>
</form>
<ul className="space-y-2">
{tasks.map((task) => (
<li key={task.id} className="flex items-center gap-3 p-3 border rounded">
<form action={toggleTask.bind(null, task.id)}>
<button type="submit">
<span className={task.completed ? 'line-through text-gray-400' : ''}>
{task.completed ? '☑' : '☐'} {task.title}
</span>
</button>
</form>
<form action={deleteTask.bind(null, task.id)} className="ml-auto">
<button type="submit" className="text-red-500 hover:text-red-700">
✕
</button>
</form>
</li>
))}
</ul>
<p className="mt-6 text-sm text-gray-500">
{tasks.length}件のタスク · {tasks.filter(t => t.completed).length}件完了
</p>
</main>
)
}
6. アプリの実行
npm run dev
http://localhost:3000を開くと、以下の機能を持つフルスタックアプリが動作します:
重要なポイント
fetch() + APIルートが必要、手動の型定義Server Actionsはフルスタック React 開発を劇的に簡素化します。本番環境では、入力バリデーション(Zodなど)、エラーハンドリング、認証を追加してください。
次のステップ
---
Techsfree ITコンサルティング — より良いWeb体験を構築します。