日本語

Next.jsのルートハンドラーを使用して強力なAPIエンドポイントを作成する方法を学びます。このガイドでは、基本的な設定から高度なテクニックまで、実践的な例やベストプラクティスを網羅しています。

Next.js ルートハンドラー: APIエンドポイント作成のための総合ガイド

Next.jsは、サーバーサイドレンダリング、静的サイト生成、そして現在のルートハンドラーといった強力な機能により、Webアプリケーションの構築方法に革命をもたらしました。ルートハンドラーは、Next.jsアプリケーション内に直接APIエンドポイントを作成するための、柔軟かつ効率的な方法を提供します。このガイドでは、ルートハンドラーの概念、その利点、そして堅牢なAPIを構築するために効果的に使用する方法について探ります。

Next.js ルートハンドラーとは?

ルートハンドラーは、Next.jsプロジェクトのappディレクトリ内に定義され、受信HTTPリクエストを処理する関数です。古いpages/apiアプローチ(APIルートを使用)とは異なり、ルートハンドラーはReactコンポーネントと並行してAPIエンドポイントを定義するための、より洗練され柔軟な方法を提供します。これらは本質的に、エッジまたは選択したサーバー環境で実行されるサーバーレス関数です。

ルートハンドラーは、リクエストの処理、データベースとの対話、レスポンスの返却を担当する、Next.jsアプリケーションのバックエンドロジックと考えてください。

ルートハンドラーを使用する利点

Next.jsプロジェクトのセットアップ

ルートハンドラーに取り掛かる前に、appディレクトリを持つNext.jsプロジェクトがセットアップされていることを確認してください。新しいプロジェクトを開始する場合は、次のコマンドを使用します:

npx create-next-app@latest my-nextjs-app

セットアッププロセス中にappディレクトリを選択して、新しいルーティングシステムを有効にしてください。

最初のルートハンドラーを作成する

JSONレスポンスを返す簡単なAPIエンドポイントを作成しましょう。appディレクトリ内に新しいディレクトリ、例えば/app/api/helloを作成します。このディレクトリ内に、route.ts(TypeScriptを使用していない場合はroute.js)という名前のファイルを作成します。

これが最初のルートハンドラーのコードです:

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
}

解説:

これで、ブラウザで/api/helloにアクセスするか、curlPostmanのようなツールを使用してこのエンドポイントにアクセスできます。

異なるHTTPメソッドの処理

ルートハンドラーは、GET、POST、PUT、DELETE、PATCH、OPTIONSなどのさまざまなHTTPメソッドをサポートしています。同じroute.tsファイル内で、各メソッドに対して別々の関数を定義できます。

// app/api/users/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 // データベースから全ユーザーを取得するロジック
 const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // サンプルデータ
 return NextResponse.json(users);
}

export async function POST(request: Request) {
 const data = await request.json(); // リクエストボディをJSONとしてパース
 // 'data'を使用してデータベースに新規ユーザーを作成するロジック
 const newUser = { id: 3, name: data.name, email: data.email }; // 例
 return NextResponse.json(newUser, { status: 201 }); // 201 Createdステータスコードと共に新しいユーザーを返す
}

解説:

リクエストデータへのアクセス

requestオブジェクトは、ヘッダー、クエリパラメータ、リクエストボディなど、受信リクエストに関するさまざまな情報へのアクセスを提供します。

ヘッダー

request.headersプロパティを使用してリクエストヘッダーにアクセスできます:

export async function GET(request: Request) {
 const userAgent = request.headers.get('user-agent');
 console.log('User Agent:', userAgent);
 return NextResponse.json({ userAgent });
}

クエリパラメータ

クエリパラメータにアクセスするには、URLコンストラクタを使用できます:

export async function GET(request: Request) {
 const url = new URL(request.url);
 const searchParams = new URLSearchParams(url.search);
 const id = searchParams.get('id');
 console.log('ID:', id);
 return NextResponse.json({ id });
}

リクエストボディ

POST、PUT、PATCHリクエストの場合、コンテントタイプに応じてrequest.json()またはrequest.text()メソッドを使用してリクエストボディにアクセスできます。

export async function POST(request: Request) {
 const data = await request.json();
 console.log('Data:', data);
 return NextResponse.json({ receivedData: data });
}

レスポンスの返却

NextResponseオブジェクトはAPIレスポンスを構築するために使用されます。ヘッダー、ステータスコード、レスポンスボディを設定するためのいくつかのメソッドを提供します。

JSONレスポンス

JSONレスポンスを返すにはNextResponse.json()メソッドを使用します:

return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });

テキストレスポンス

プレーンテキストのレスポンスを返すにはnew Response()コンストラクタを使用します:

return new Response('Hello, world!', { status: 200, headers: { 'Content-Type': 'text/plain' } });

リダイレクト

ユーザーを別のURLにリダイレクトするにはNextResponse.redirect()を使用します:

import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.redirect(new URL('/new-location', request.url));
}

ヘッダーの設定

NextResponse.json()またはnew Response()headersオプションを使用してカスタムヘッダーを設定できます:

return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });

ミドルウェア統合

ミドルウェアを使用すると、リクエストがルートハンドラーによって処理される前にコードを実行できます。これは認証、認可、ロギングなどの横断的関心事に役立ちます。

ミドルウェアを作成するには、appディレクトリまたはそのサブディレクトリにmiddleware.ts(またはmiddleware.js)という名前のファイルを作成します。ミドルウェアはそのディレクトリおよびそのサブディレクトリ内のすべてのルートに適用されます。

// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth-token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/protected/:path*'], // このミドルウェアを/protected/で始まるパスに適用
};

解説:

エラーハンドリング

堅牢なAPIを構築するためには、適切なエラーハンドリングが不可欠です。try...catchブロックを使用して例外を処理し、適切なエラーレスポンスを返すことができます。

export async function GET(request: Request) {
 try {
 // エラーをシミュレート
 throw new Error('Something went wrong!');
 } catch (error: any) {
 console.error('Error:', error);
 return NextResponse.json({ error: error.message }, { status: 500 });
 }
}

解説:

ストリーミングレスポンス

ルートハンドラーはストリーミングレスポンスをサポートしており、クライアントにデータを段階的に送信することができます。これは特に大規模なデータセットや長時間実行されるプロセスに役立ちます。

import { Readable } from 'stream';
import { NextResponse } from 'next/server';

async function* generateData() {
 for (let i = 0; i < 10; i++) {
 await new Promise(resolve => setTimeout(resolve, 500)); // 遅延をシミュレート
 yield `Data chunk ${i}\n`;
 }
}

export async function GET(request: Request) {
 const readableStream = Readable.from(generateData());

 return new Response(readableStream, {
 headers: { 'Content-Type': 'text/plain; charset=utf-8' },
 });
}

解説:

認証と認可

APIエンドポイントのセキュリティ確保は非常に重要です。ミドルウェアを使用するか、ルートハンドラー内で直接、認証と認可を実装できます。

認証

認証は、リクエストを行っているユーザーの身元を確認します。一般的な認証方法には以下があります:

以下は、ミドルウェアを使用したJWT認証の例です:

// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';

const secret = process.env.JWT_SECRET || 'your-secret-key'; // 強力でランダムに生成されたシークレットに置き換えてください

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth-token')?.value;

 if (!token) {
 return NextResponse.json({ message: 'Authentication required' }, { status: 401 });
 }

 try {
 jwt.verify(token, secret);
 return NextResponse.next();
 } catch (error) {
 return NextResponse.json({ message: 'Invalid token' }, { status: 401 });
 }
}

export const config = {
 matcher: ['/api/protected/:path*'],
};

認可

認可は、ユーザーがどのリソースにアクセスできるかを決定します。これは通常、ロールや権限に基づいています。

ルートハンドラー内でユーザーのロールや権限を確認し、アクセス権がない場合はエラーを返すことで認可を実装できます。

// app/api/admin/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 // トークンやセッションからユーザーのロールを取得する関数があると仮定
 const userRole = await getUserRole(request);

 if (userRole !== 'admin') {
 return NextResponse.json({ message: 'Unauthorized' }, { status: 403 });
 }

 // 管理者データを取得するロジック
 const adminData = { message: 'Admin data' };
 return NextResponse.json(adminData);
}

async function getUserRole(request: Request): Promise {
 // リクエストからユーザーのロールを抽出する実際のロジックに置き換えてください
 // JWTトークンの検証やセッションの確認などが考えられます
 return 'admin'; // 例:デモンストレーション用のハードコードされたロール
}

ルートハンドラーのデプロイ

ルートハンドラーは、選択したホスティングプロバイダー上でサーバーレス関数としてデプロイされます。Next.jsはVercel、Netlify、AWSなど、さまざまなデプロイプラットフォームをサポートしています。

Vercelの場合、デプロイはGitリポジトリをVercelに接続し、コードをプッシュするだけと簡単です。Vercelは自動的にNext.jsプロジェクトを検出し、ルートハンドラーをサーバーレス関数としてデプロイします。

高度なテクニック

エッジ関数

ルートハンドラーはエッジ関数としてデプロイでき、これはユーザーに近いCDNのエッジで実行されます。これにより、遅延が大幅に削減され、パフォーマンスが向上します。

ルートハンドラーをエッジ関数としてデプロイするには、route.tsファイルにedgeランタイムを追加します:

export const runtime = 'edge';

import { NextResponse } from 'next/server';

export async function GET(request: Request) {
 return NextResponse.json({ message: 'Hello from the Edge!' });
}

サーバーアクション

サーバーアクションを使用すると、Reactコンポーネントから直接サーバーサイドのコードを実行できます。ルートハンドラーとサーバーアクションはシームレスに連携し、複雑なアプリケーションを簡単に構築できます。

以下は、サーバーアクションを使用してルートハンドラーを呼び出す例です:

// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';

async function handleSubmit(data: FormData) {
 'use server';

 const name = data.get('name');
 const email = data.get('email');

 const response = await fetch('/api/users', {
 method: 'POST',
 body: JSON.stringify({ name, email }),
 });

 if (response.ok) {
 router.refresh(); // 変更を反映するためにページをリフレッシュ
 }
}

export default function MyComponent() {
 const router = useRouter();

 return (
 




); }

キャッシング

キャッシングはAPIエンドポイントのパフォーマンスを大幅に向上させることができます。Cache-Controlヘッダーを使用して、ブラウザやCDNによるレスポンスのキャッシュ方法を制御できます。

return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });

この例では、Cache-Controlヘッダーをpublic, max-age=3600に設定しており、ブラウザとCDNにレスポンスを1時間キャッシュするように指示します。

ベストプラクティス

実世界の例

以下は、ルートハンドラーがどのように使用できるかの実世界の例です:

国際Eコマースの例: ユーザーの国に基づいて製品価格を取得するために使用されるルートハンドラー。エンドポイントはリクエストの地理情報(IPアドレスから導出)を使用してユーザーの場所を特定し、適切な通貨で価格を返すことができます。これは、ローカライズされたショッピング体験に貢献します。

グローバル認証の例: 世界中のユーザー向けに多要素認証(MFA)を実装するルートハンドラー。これには、SMSコードの送信や認証アプリの使用が含まれ、同時に異なる地域のプライバシー規制や通信インフラを尊重します。

多言語コンテンツ配信: ユーザーの優先言語でコンテンツを配信するルートハンドラー。これはリクエストの`Accept-Language`ヘッダーから判断できます。この例は、適切なUTF-8エンコーディングと、必要に応じた右から左への言語サポートの必要性を強調しています。

結論

Next.jsのルートハンドラーは、Next.jsアプリケーション内に直接APIエンドポイントを作成するための強力で柔軟な方法を提供します。ルートハンドラーを活用することで、堅牢なAPIを簡単に構築し、バックエンドロジックをReactコンポーネントとコロケーションさせ、ミドルウェア、ストリーミング、エッジ関数などの機能を利用できます。

この総合ガイドでは、基本的な設定から高度なテクニックまでを網羅しました。このガイドで概説されたベストプラクティスに従うことで、安全で、パフォーマンスが高く、保守可能な高品質のAPIを構築できます。

Next.js ルートハンドラー: APIエンドポイント作成のための総合ガイド | MLOG