2025年2月28日(金曜日)
Next.jsでAPIを構築する
投稿者このガイドでは、Next.js で API を構築する方法について説明します。プロジェクトのセットアップ、App Router と Route Handlers の理解、複数の HTTP メソッドの処理、動的ルーティングの実装、再利用可能なミドルウェアロジックの作成、そして専用の API レイヤーを立ち上げるタイミングの決定などが含まれます。
- 1. はじめに
- 2. Next.jsでAPIを構築する理由(とタイミング)
- 3. Route Handlers でAPIを作成する
- 4. Web APIの操作
- 5. 動的ルーティング
- 6. Next.jsをプロキシまたはフォワーディングレイヤーとして使用する
- 7. 共有の「ミドルウェア」ロジックを構築する
- 8. デプロイと「SPAモード」の考慮事項
- 9. APIエンドポイントの作成をスキップするタイミング
- 10. まとめ
- 結論
- よくある質問
1. はじめに
1.1 Next.jsアプリを作成する
新たに開始する場合は、Next.js プロジェクトを次のコマンドで作成できます
npx create-next-app@latest --api
注:
--api
フラグは、新しいプロジェクトのapp/
フォルダーに例のroute.ts
を自動的に含め、API エンドポイントの作成方法を示します。
1.2 App Router と Pages Router
- Pages Router: 歴史的に、Next.jsはAPIに
pages/api/*
を使用していました。このアプローチは、Node.js のリクエスト/レスポンスオブジェクトと Express のようなAPIに依存していました。 - App Router (デフォルト): Next.js 13 で導入された App Router は、Web標準の Request/Response API を完全に採用しています。
pages/api/*
の代わりに、route.ts
またはroute.js
ファイルをapp/
ディレクトリ内のどこにでも配置できるようになりました。
切り替える理由とは? App Router の「Route Handlers」は、Node.js 固有の API ではなく、Web Platform の Request/Response API に基づいています。これにより、学習が簡素化され、摩擦が軽減され、さまざまなツールで知識を再利用できるようになります。
2. Next.jsでAPIを構築する理由(とタイミング)
-
複数のクライアント向けの公開API
- Next.js Web アプリ、別のモバイルアプリ、または任意のサードパーティサービスで利用される公開 API を構築できます。たとえば、React ウェブサイトと React Native モバイルアプリの両方で /api/users からデータを取得できます。
-
既存のバックエンドへのプロキシ
- 既存のバックエンドサービスへのプロキシが必要な場合があります。外部のマイクロサービスを単一のエンドポイントの背後に隠したり、統合したりしたい場合があります。Next.js の Route Handlers は、既存の別のバックエンドへのプロキシまたはミドルレイヤーとして機能できます。たとえば、リクエストを傍受し、認証を処理し、データを変換してから、アップストリーム API にリクエストを渡すことができます。
-
Webhookと統合
- 外部からのコールバックや 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) {
// For example, fetch data from your DB here
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) {
// Parse the request body
const body = await request.json();
const { name } = body;
// e.g. Insert new user into your DB
const newUser = { id: Date.now(), name };
return new Response(JSON.stringify(newUser), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
}
これで、/api/users
への GET リクエストはユーザーリストを返し、同じ URL への POST
リクエストは新しいユーザーを挿入します。
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'); // e.g. `/api/search?query=hello`
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) {
// 1. Using 'next/headers' helpers
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const referer = headersList.get('referer');
// 2. Using the standard Web APIs
const userAgent = request.headers.get('user-agent');
return new Response(JSON.stringify({ token, referer, userAgent }), {
headers: { 'Content-Type': 'application/json' },
});
}
cookies()
および headers()
関数は、Next.js の他のサーバーサイドコード間で共有ロジックを再利用する場合に役立ちます。Next.js は、ベースとなる Web API を拡張した NextRequest
および NextResponse
も提供していることに気づくでしょう。
5. 動的ルーティング
動的なパス (例: /api/users/:id
) を作成するには、フォルダー構造で動的セグメントを使用します
app
└── api
└── users
└── [id]
└── route.ts
このファイルは /api/users/123
のような URL に対応し、123
がパラメータとして取得されます。
import { NextRequest } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// e.g. Query a database for user with ID `id`
return new Response(JSON.stringify({ id, name: `User ${id}` }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// e.g. Delete user with ID `id` in DB
return new Response(null, { status: 204 });
}
ここで、params.id
が動的セグメントを提供します。
6. Next.jsをプロキシまたはフォワーディングレイヤーとして使用する
一般的なシナリオは、既存のバックエンドサービスをプロキシすることです。リモートサーバーやバックエンドにリクエストを送信する前に、リクエストの認証、ロギングの処理、データの変換を行うことができます
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const response = await fetch('https://example.com/api/data', {
// Optional: forward some headers, add auth tokens, etc.
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
// Transform or forward the response
const data = await response.json();
const transformed = { ...data, source: 'proxied-through-nextjs' };
return new Response(JSON.stringify(transformed), {
headers: { 'Content-Type': 'application/json' },
});
}
これでクライアントは /api/external
を呼び出すだけでよく、残りは Next.js が処理します。これは「Backend for Frontend」または BFF とも呼ばれることがあります。
7. 共有の「ミドルウェア」ロジックを構築する
複数の Route Handlers で同じロジック (例: 認証チェック、ロギング) を適用したい場合、ハンドラーをラップする再利用可能な関数を作成できます
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' },
});
}
// If authenticated, call the original handler
return handler(req, context);
};
}
次に、Route Handler で
import { NextRequest } from 'next/server';
import { withAuth } from '@/lib/with-auth';
async function secretGET(request: NextRequest) {
return new Response(JSON.stringify({ secret: 'Here be dragons' }), {
headers: { 'Content-Type': 'application/json' },
});
}
export const GET = withAuth(secretGET);
8. デプロイと「SPAモード」の考慮事項
8.1 標準のNode.jsデプロイメント
next start
を使用した標準の Next.js サーバーデプロイメントでは、Route Handlers、Server Components、Middleware などの機能を使用でき、動的なリクエスト時情報を活用できます。
追加の設定は不要です。詳細については、デプロイ を参照してください。
8.2 SPA/静的エクスポート
Next.js は、サイト全体を静的シングルページアプリケーション (SPA) として出力することもサポートしています。
これを有効にするには、次のように設定します。
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
};
export default nextConfig;
静的エクスポートモードでは、Next.js は純粋な静的 HTML、CSS、JS を生成します。サーバーサイドのコード (API エンドポイントなど) を実行することはできません。API がまだ必要な場合は、個別にホストする必要があります (例: スタンドアロンの Node.js サーバー)。
注
- GET Route Handlers は、動的なリクエストデータに依存しない場合、静的にエクスポートできます。それらは出力フォルダー内の静的ファイルになります。
- その他のすべてのサーバー機能 (動的リクエスト、クッキーの書き換えなど) は、純粋な SPA エクスポートではサポートされていません。
8.3 VercelへのAPIデプロイ
Next.js アプリケーションを Vercel にデプロイする場合、API のデプロイに関するガイドがあります。これには、Vercel Firewall を介したプログラムによるレート制限など、その他の Vercel 機能も含まれます。Vercel はまた、API アプローチで一般的に必要とされるCron Jobsも提供しています。
9. APIエンドポイントの作成をスキップするタイミング
App Router のReact Server Components を使用すると、公開エンドポイントを公開することなく、サーバー上で直接データを取得できます。
// (Server Component)
export default async function UsersPage() {
// This fetch runs on the server (no client-side code needed here)
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
など) をエクスポートする。 Request
オブジェクトとやり取りし、Response
を返すためにWeb標準APIを使用する。- 他のクライアントがデータを消費する必要がある場合、またはバックエンドサービスをプロキシする必要がある場合に公開APIを構築する。
- クライアントから新しい API ルートをフェッチする (例: Client Component 内から、または
fetch('/api/...')
を使用して)。 - または、Server Component がデータを取得できる場合は、API の作成を完全にスキップする。
- 認証やその他の繰り返されるロジックのために、共有の「ミドルウェア」パターン (例:
withAuth()
) を追加する。 - サーバー機能のために Node.js が利用可能な環境にデプロイするか、静的 SPA のみが必要な場合は静的にエクスポートする。
結論
Next.js のApp Router と Route Handlers を使用すると、Web Platform を直接活用した柔軟でモダンな API 構築方法が得られます。これにより、次のことが可能になります。
- Web、モバイル、またはサードパーティのクライアントと共有するための完全な公開 API を作成する。
- 既存の外部サービスへの呼び出しをプロキシし、カスタマイズする。
- 認証、ロギング、または任意の繰り返されるロジックのための再利用可能な「ミドルウェア」レイヤーを実装する。
[id]
セグメントフォルダー構造を使用してリクエストを動的にルーティングする。
よくある質問
Server Actionsについては?
Server Actions は、クライアントから呼び出せる自動生成された POST
API ルートと考えることができます。
これらは、データの作成、更新、削除などの変更操作のために設計されています。定義された API ルートへの明示的な fetch
を行うのではなく、通常の JavaScript 関数のように Server Action を呼び出します。
ネットワークリクエストは依然として発生しますが、明示的に管理する必要はありません。URL パスは自動生成され、暗号化されているため、ブラウザで /api/users
のようなルートに手動でアクセスすることはできません。
Server Actions を使用し、かつ公開 API を公開する予定がある場合、コアロジックをデータアクセスレイヤーに移動し、Server Action と API ルートの両方から同じロジックを呼び出すことをお勧めします。
Route HandlersでTypeScriptを使用できますか?
はい、Route Handlers で TypeScript を使用できます。たとえば、route
ファイルで Request
および Response
の型を定義します。
Next.js と TypeScript の詳細については、こちらをご覧ください。
認証のベストプラクティスは何ですか?
詳細については、認証ドキュメントをご覧ください。