このガイドでは、Next.jsでAPIを構築する方法について説明します。プロジェクトのセットアップ、App RouterとRoute Handlersの理解、複数のHTTPメソッドの処理、動的ルーティングの実装、再利用可能なミドルウェアロジックの作成、そして専用のAPIレイヤーをいつ立ち上げるかの判断について説明します。
1. はじめに
1.1 Next.jsアプリの作成
新しく始める場合は、以下のコマンドで新しいNext.jsプロジェクトを作成できます:
npx create-next-app@latest --api
注意: --apiフラグを使用すると、新しいプロジェクトのapp/フォルダにroute.tsの例が自動的に含まれ、APIエンドポイントの作成方法が示されます。
1.2 App Router vs. Pages Router
Pages Router: 従来、Next.jsはAPIにpages/api/*を使用していました。このアプローチはNode.jsのrequest/responseオブジェクトとExpress風のAPIに依存していました。
App Router(デフォルト): Next.js 13で導入されたApp Routerは、Web標準のRequest/Response APIを完全に採用しています。pages/api/*の代わりに、app/ディレクトリ内の任意の場所にroute.tsまたはroute.jsファイルを配置できるようになりました。
なぜ切り替えるのか? App Routerの「Route Handlers」は、Node.js固有のAPIではなく、Web PlatformのRequest/Response APIに依存しています。これにより学習が簡素化され、摩擦が減り、異なるツール間で知識を再利用できるようになります。
2. Next.jsでAPIを構築する理由(とタイミング)
複数のクライアント向けのパブリックAPI
Next.jsウェブアプリ、別のモバイルアプリ、またはサードパーティサービスによって消費されるパブリックAPIを構築できます。例えば、ReactウェブサイトとReact Nativeモバイルアプリの両方で/api/usersからフェッチすることができます。
既存のバックエンドへのプロキシ
外部のマイクロサービスを単一のエンドポイントの背後に隠したり統合したりしたい場合があります。Next.jsのRoute Handlersは、他の既存のバックエンドへのプロキシまたは中間レイヤーとして機能できます。
WebhookとIntegration
外部からのコールバックやwebhook(例:Stripe、GitHub、Twilio)を受信する場合、Route Handlersで処理できます。
カスタム認証
セッション、トークン、その他の認証ロジックが必要な場合、Next.jsのAPIレイヤーでクッキーの保存、ヘッダーの読み取り、適切なデータでの応答ができます。
注意: 自分のNext.jsアプリのためだけにサーバーサイドデータフェッチが必要で(そのデータを外部で共有する必要がない)場合、Server Componentsがレンダリング中に直接データをフェッチするのに十分かもしれません—別のAPIレイヤーは不要です。
3. Route Handlersを使用したAPIの作成
3.1 基本的なファイル設定
App Router(app/)では、ルートを表すフォルダを作成し、その中にroute.tsファイルを配置します。例えば、/api/usersにエンドポイントを作成するには:
app
└── api
└── users
└── route.ts
3.2 1つのファイルでの複数のHTTPメソッド
Pages Router APIルート(単一のデフォルトエクスポートを持つ)とは異なり、同じファイルから異なるHTTPメソッドを表す複数の関数をエクスポートできます。
export async function GET(request: Request) {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
return new Response(JSON.stringify(users), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
export async function POST(request: Request) {
const body = await request.json();
const { name } = body;
const newUser = { id: Date.now(), name };
return new Response(JSON.stringify(newUser), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
}
4. Web APIの操作
4.1 RequestとResponseの直接使用
デフォルトでは、Route Handlerメソッド(GET、POSTなど)は標準のRequestオブジェクトを受け取り、標準のResponseオブジェクトを返す必要があります。
4.2 クエリパラメータ
import { NextRequest } from 'next/server';
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get('query');
return new Response(
JSON.stringify({ result: `You searched for: ${query}` }),
{
headers: { 'Content-Type': 'application/json' },
},
);
}
4.3 ヘッダーとクッキー
import { NextRequest } from 'next/server';
import { cookies, headers } from 'next/headers';
export async function GET(request: NextRequest) {
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const referer = headersList.get('referer');
const userAgent = request.headers.get('user-agent');
return new Response(
JSON.stringify({ token, referer, userAgent }),
{
headers: { 'Content-Type': 'application/json' },
}
);
}
5. 動的ルート
動的パス(例:/api/users/:id)を作成するには、フォルダ構造でDynamic Segmentsを使用します:
app
└── api
└── users
└── [id]
└── route.ts
import { NextRequest } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
return new Response(
JSON.stringify({ id, name: `User ${id}` }),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
}
);
}
6. Next.jsをプロキシまたは転送レイヤーとして使用
既存のバックエンドサービスをプロキシする一般的なシナリオです。リクエストを認証し、ログを処理し、リモートサーバーやバックエンドに送信する前にデータを変換できます:
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const response = await fetch('https://example.com/api/data', {
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`
},
});
const data = await response.json();
const transformed = {
...data,
source: 'proxied-through-nextjs'
};
return new Response(JSON.stringify(transformed), {
headers: { 'Content-Type': 'application/json' },
});
}
7. 共有「ミドルウェア」ロジックの構築
複数のRoute Handlerで同じロジック(認証チェック、ログなど)を適用したい場合、ハンドラーをラップする再利用可能な関数を作成できます:
import { NextRequest } from 'next/server';
type Handler = (req: NextRequest, context?: any) => Promise<Response>;
export function withAuth(handler: Handler): Handler {
return async (req, context) => {
const token = req.cookies.get('token')?.value;
if (!token) {
return new Response(
JSON.stringify({ error: 'Unauthorized' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' },
}
);
}
return handler(req, context);
};
}
8. デプロイメントと「SPAモード」の考慮事項
8.1 標準Node.jsデプロイメント
next startを使用した標準のNext.jsサーバーデプロイメントでは、Route Handlers、Server Components、Middlewareなどの機能を使用でき、動的なリクエスト時情報を活用できます。追加の設定は不要です。
8.2 SPA/静的エクスポート
Next.jsは、サイト全体を静的なSingle-Page Application(SPA)として出力することもサポートしています:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
};
export default nextConfig;
静的エクスポートモードでは、Next.jsは純粋に静的なHTML、CSS、JSを生成します。サーバーサイドコード(APIエンドポイントなど)は実行できません。
8.3 VercelでのAPIデプロイ
Next.jsアプリケーションをVercelにデプロイする場合、APIのデプロイに関するガイドがあります。これには、Vercel Firewallを通じたプログラマティックなレート制限などの他のVercel機能も含まれます。
9. APIエンドポイントの作成をスキップするタイミング
App RouterのReact Server Componentsを使用すると、パブリックエンドポイントを公開することなく、サーバー上で直接データをフェッチできます:
export default async function UsersPage() {
const res = await fetch('https://api.example.com/users');
const data = await res.json();
return (
<main>
<h1>Users</h1>
<ul>
{data.map((user: any) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</main>
);
}
データがNext.jsアプリ内でのみ使用される場合、パブリックAPIは全く必要ないかもしれません。
10. すべてをまとめる
- 新しいNext.jsプロジェクトを作成:
npx create-next-app@latest --api
app/ディレクトリ内にRoute Handlersを追加(例:app/api/users/route.ts)
- 同じファイルでHTTPメソッド(
GET、POST、PUT、DELETEなど)をエクスポート
- Web標準APIを使用して
Requestオブジェクトと対話し、Responseを返す
- 他のクライアントがデータを消費する必要がある場合、またはバックエンドサービスをプロキシする場合は、パブリックAPIを構築
結論
Next.jsのRoute Handlersは、モダンなWeb標準を活用してAPIを構築するための強力で柔軟な方法を提供します。プロキシレイヤーから完全なAPIまで、様々なユースケースに対応できます。
よくある質問
Server Actionsについてはどうですか?
Server ActionsはフォームやミューテーションのためのNext.js固有の機能で、Route Handlersと組み合わせて使用できます。
Route HandlersでTypeScriptを使用できますか?
はい、Route HandlersはTypeScriptを完全にサポートしており、型安全性を提供します。
認証のベストプラクティスは何ですか?
JWT、セッション、またはサードパーティ認証プロバイダーを使用し、適切なミドルウェアパターンを実装することが推奨されます。