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を開くと、以下の機能を持つフルスタックアプリが動作します:

  • ✅ サーバーサイドレンダリング
  • ✅ データベース永続化(SQLite + Prisma)
  • ✅ Server Actions(APIルート不要)
  • ✅ 自動再検証
  • 重要なポイント

  • 従来のAPI方式: fetch() + APIルートが必要、手動の型定義
  • Server Actions方式: 関数を直接呼び出し、エンドツーエンドのTypeScript型安全性
  • Server Actionsはフルスタック React 開発を劇的に簡素化します。本番環境では、入力バリデーション(Zodなど)、エラーハンドリング、認証を追加してください。

    次のステップ

  • Zodでフォームバリデーションを追加
  • NextAuth.jsで認証を統合
  • VercelにPostgreSQLデータベースでデプロイ

---

Techsfree ITコンサルティング — より良いWeb体験を構築します。