😀

【Next.js 14】Server Actionsで実現するモダンなフォーム処理

に公開

はじめに

ウェブアプリケーション開発において、フォーム処理は最も一般的でありながら複雑なタスクの1つです。Next.js 14で正式に導入されたServer Actionsは、このフォーム処理を劇的に簡素化する新機能です。本記事では、Server Actionsを活用したモダンなフォーム処理の実装方法について解説します。

Server Actionsとは

Server ActionsはNext.js 14で正式リリースされた機能で、クライアントサイドから直接サーバーサイドの関数を呼び出すことができます。これにより、APIルートを自前で作成する必要がなくなり、フォーム送信やデータ処理をより直感的に記述できるようになります。

主な特徴:

  • クライアントコンポーネントからサーバーサイドコードを直接実行可能
  • TypeScriptの型安全性を保ったまま開発可能
  • プログレッシブエンハンスメントをサポート
  • デフォルトでCSRF対策が組み込まれている

基本的な実装例

// app/actions.ts
"use server";

export async function createPost(formData: FormData) {
  const title = formData.get("title");
  const content = formData.get("content");
  
  // データベースへの保存処理など
  await db.post.create({
    data: { title, content }
  });
  
  revalidatePath("/posts");
}
// app/page.tsx
import { createPost } from "@/app/actions";

export default function PostForm() {
  return (
    <form action={createPost}>
      <input type="text" name="title" required />
      <textarea name="content" required />
      <button type="submit">作成</button>
    </form>
  );
}

メリットとユースケース

1. シンプルなデータ送信

従来のAPIルート作成やfetch処理が不要に。フォーム送信が劇的に簡潔になります。

2. リアルタイムバリデーション

useFormStateと組み合わせることで、サーバーサイドバリデーションを簡単に実装可能:

"use client";

import { useFormState } from "react-dom";
import { createPost } from "./actions";

const initialState = { errors: {} };

export function PostForm() {
  const [state, formAction] = useFormState(createPost, initialState);
  
  return (
    <form action={formAction}>
      <input name="title" />
      {state.errors?.title && <p>{state.errors.title}</p>}
      {/* ... */}
    </form>
  );
}

3. 楽観的UI更新

useOptimisticフックと組み合わせて、投稿前にUIを即時更新:

"use client";

import { useOptimistic } from "react";
import { sendMessage } from "./actions";

export function Chat({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) => [...state, { text: newMessage, sending: true }]
  );

  async function formAction(formData: FormData) {
    const message = formData.get("message");
    addOptimisticMessage(message);
    await sendMessage(message);
  }

  return (
    <>
      {optimisticMessages.map((msg, i) => (
        <div key={i}>{msg.text} {msg.sending && "送信中..."}</div>
      ))}
      <form action={formAction}>
        <input type="text" name="message" />
        <button type="submit">送信</button>
      </form>
    </>
  );
}

ベストプラクティス

  1. 大規模アプリケーションでの整理

    • 関連するServer Actionsをactionsディレクトリにグループ化
    • 1ファイルにつき1つのリソース(posts/actions.tsなど)
  2. セキュリティ対策

    • 常に"use server"ディレクティブを使用
    • 権限チェックを必ず実装
    • 入力値の検証をサーバーサイドで実施
  3. パフォーマンス最適化

    • 頻繁に変更されるデータにはdynamic = 'force-dynamic'
    • 大きなファイルアップロードにはuseUploadThingなどのライブラリを検討

既存手法との比較

方法 コード量 パフォーマンス 開発体験
APIルート + fetch 多い 普通 複雑
tRPC 普通 良い 良い
Server Actions 少ない 優れている 非常に良い

制限事項と注意点

  • 現時点では、Server Actionsのサイズ制限(1MB)がある
  • 複雑な非同期処理には向かない場合がある
  • エラーハンドリングには注意が必要
  • 現状、Edge Runtimeでは完全な機能が利用できない

まとめ

Next.js 14のServer Actionsは、フォーム処理とデータ変更のパラダイムを変える強力な機能です。APIルートの作成やクライアントサイドの複雑な状態管理が不要になり、開発者はビジネスロジックに集中できるようになります。

まだ発展途上の機能ではありますが、今後さらに進化していくことが予想されます。フォームを多用するアプリケーション開発においては、ぜひこの新しいアプローチを試してみてください。

Discussion