本記事は TypeScript + Next.js 学習リポジトリ の実装をベースに、App Router におけるサーバー処理の2つの実装パターン「API Routes」と「Server Action」を解説します。
はじめに
「フォーム送信を受け取って DB に保存したい」「外部APIを叩きたい」といった サーバー側の処理 をNext.jsで実装する方法は、大きく分けて2つあります。
| 実装方法 | 概要 |
|---|---|
| API Routes | ファイルシステムベースのルーティングでAPIエンドポイントを作成する従来型のアプローチ |
| Server Action | 同じコンポーネント内に「フロント」と「サーバー処理」を共存させられる新しいアプローチ |
それぞれ実装してみて、違いを掴みましょう。
1. API Routes
概要
ファイルシステムベースのルーティングで APIのエンドポイントを作成できる機能
通常のページと同様に、フォルダ構造でURL(エンドポイント)が決まります。
違いは、page.tsx ではなく route.ts という予約ファイルを作成する点です。
ディレクトリ構造
app/
└─ api/
└─ create/
└─ route.ts → POST /api/create などとして呼び出せる
💡
/apiや/createといった命名は 慣習的なもの であり必須ではありません。ただしroute.tsは 予約ファイル なのでこのファイル名であることが必須です。
サーバー側の実装例(app/api/create/route.ts)
import { NextResponse } from 'next/server';
// 関数名を HTTP メソッド名(UPPER CASE)にし、引数にリクエストデータを受け取る
export async function POST(request: Request) {
const data = await request.json();
// 以下のログはサーバー側で実行されるため、
// ブラウザのコンソールではなく「ターミナル」で動作確認する
console.log('API Routesで受け取りました', data.name);
return NextResponse.json({ message: '成功しました' });
}
ポイント
-
関数名 = HTTPメソッド名(大文字):
POST/GET/PUT/DELETE/PATCHなど - 引数で
Requestオブジェクトを受け取り、request.json()などで body を取り出す - レスポンスは
NextResponse.json()で返す -
console.logはサーバー側で動くため、ブラウザのDevToolsではなくターミナルに出力される
クライアント側の呼び出し(app/api-routes-user/page.tsx)
通常の fetch() でエンドポイントを叩くだけです。
'use client'; // クライアントコンポーネント宣言
import { SubmitEvent } from 'react';
function ApiRoutesUser() {
const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
const form = new FormData(e.currentTarget);
const name = form.get('name');
// ファイルシステムベースのURLで呼び出す
await fetch('/api/create', {
method: 'POST', // route.ts の関数名(POST)に対応
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
};
return (
<form onSubmit={handleSubmit}>
<input type='text' name='name' />
<button type='submit'>送信</button>
</form>
);
}
export default ApiRoutesUser;
動作フロー
[ブラウザ] フォーム送信
↓
[ブラウザ] handleSubmit が起動 → fetch('/api/create', { method: 'POST', ... })
↓
[サーバー] /app/api/create/route.ts の POST 関数が実行される
↓
[サーバー] レスポンスを返却
↓
[ブラウザ] レスポンスを受信
2. Server Action
概要
同じコンポーネント内で バックエンドの処理と呼び出しイベントの両方を実装できる機能
API Routes は「サーバー処理用のファイルを別に作る」必要がありましたが、Server Action では コンポーネントの中に直接サーバー処理を書ける のが最大の特徴です。
実装例(app/server-action-user/page.tsx)
function ServerActionUser() {
const createAction = async (formData: FormData) => {
'use server'; // ← この一行でサーバー側で実行される関数になる
const name = formData.get('name');
console.log('ServerActionで実行されました', name);
};
return (
// form の action 属性に Server Action の関数を直接渡す
<form action={createAction}>
<input type='text' name='name' />
<button type='submit'>送信</button>
</form>
);
}
export default ServerActionUser;
ポイント
-
'use server'ディレクティブで、その関数がサーバー上で実行されることを宣言 -
<form action={createAction}>のように、関数を直接action属性に渡せる -
fetchでURLを指定する必要がない -
'use client'を付けていなくても(サーバーコンポーネントでも)動作する
動作フロー
[ブラウザ] フォーム送信
↓
[Next.js が裏側で自動的にエンドポイントを用意して呼び出す]
↓
[サーバー] createAction が実行される
↓
[ブラウザ] レスポンス受信(必要なら自動でページが更新される)
エンドポイントのURLや HTTP メソッドの管理が不要になり、コードがシンプルになります。
API Routes と Server Action の比較
| 観点 | API Routes | Server Action |
|---|---|---|
| 実装場所 | app/api/.../route.ts |
コンポーネント内に直接記述 |
| 呼び出し方 | fetch('/api/...') |
<form action={fn}> または直接呼び出し |
| HTTPメソッドの指定 | 必要(関数名 = メソッド名) | 不要(Next.jsが自動で処理) |
| 別アプリからの呼び出し | 可能(普通のREST API) | 不可(Next.js内部で完結) |
| コードの見通し | フロントとバックが別ファイル | 同じファイルでまとまる |
| 主なユースケース | 外部公開API、複雑なAPI設計 | フォーム送信、内部処理 |
使い分けの目安
-
Server Action を使うべきケース
- フォーム送信のようにNext.jsアプリ内で完結する処理
- データ更新後にページを再描画したい場合(
revalidatePathなどと相性◎) - シンプルにコードを書きたい場合
-
API Routes を使うべきケース
- 外部のクライアント(モバイルアプリなど)からも叩きたいAPI
- Webhookの受け口
- 細かいHTTPメソッド/ヘッダーの制御が必要な場合
まとめ
| 機能 | キーワード | 一言で |
|---|---|---|
| API Routes |
route.ts / export async function POST
|
ファイルベースの REST API |
| Server Action |
'use server' / <form action>
|
コンポーネント内に書けるサーバー処理 |
App Router 時代の Next.js では、「アプリ内部だけならServer Action、外部公開や特殊な要件があるならAPI Routes」 が基本的な使い分けになります。
特に Server Action は、キャッシュの再検証(revalidatePath など)と組み合わせることで、極めて簡潔にデータ更新フローを書けるのが大きな魅力です。