Next.js API Routesを利用して、アプリ内に直接サーバーレスバックエンドを構築する方法を解説します。基本設定から認証、データ永続化などの高度な技術までを網羅したガイドです。
Next.js API Routes: 手軽にバックエンドを構築する
Next.jsは、その強力な機能と直感的な構造でフロントエンド開発に革命をもたらしました。しかし、バックエンド開発も大幅に簡素化できることをご存知でしたか?Next.js API Routesを使用すると、Next.jsアプリケーション内に直接サーバーレスAPIエンドポイントを作成でき、多くの場合、別のバックエンドサーバーは不要になります。この包括的なガイドでは、Next.js API Routesを使用して堅牢でスケーラブルなバックエンドを構築するプロセスを順を追って説明します。
Next.js API Routesとは?
API Routesは、Next.jsプロジェクトの/pages/api
ディレクトリ内に作成するサーバーレス関数です。これらの関数は、従来のバックエンドAPIと同様に、受信したHTTPリクエストを処理し、レスポンスを返します。主な違いは、これらがサーバーレス関数としてデプロイされる点であり、サーバーやインフラストラクチャを管理する必要がないことを意味します。
これらを、Next.jsのフロントエンドとシームレスに統合された、軽量でオンデマンドなバックエンド関数だと考えてください。
Next.js API Routesを使用するメリット
- 開発の簡素化: フロントエンドとバックエンドの両方のコードを、JavaScriptまたはTypeScriptを使用して同じプロジェクトで記述します。異なるプロジェクトや技術間でのコンテキストスイッチングはもうありません。
- サーバーレスアーキテクチャ: サーバーレスコンピューティングのスケーラビリティ、信頼性、コスト効率の恩恵を受けられます。消費したリソースに対してのみ支払います。
- 簡単なデプロイ: VercelやNetlifyなどのプラットフォームを使用して、単一のコマンドでアプリケーション全体(フロントエンドとバックエンド)をデプロイできます。
- 組み込みのセキュリティ: Next.jsとサーバーレスプラットフォームは、APIエンドポイントを保護するための組み込みセキュリティ機能を提供します。
- パフォーマンスの向上: API Routesはユーザーに近い場所にデプロイできるため、遅延が減少しパフォーマンスが向上します。これは特にグローバルなユーザーにとって有益です。
- コードの再利用性: フロントエンドとバックエンド間でコードを共有し、コードの重複を減らし、保守性を向上させます。
Next.js API Routesをはじめよう
JSONレスポンスを返す簡単なAPIルートを作成してみましょう。まず、Next.jsプロジェクトがセットアップされていることを確認してください。まだの場合は、以下を使用して作成します。
npx create-next-app my-app
cd my-app
次に、/pages/api
ディレクトリ内にhello.js
という名前のファイルを作成します。
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' })
}
このコードは、「John Doe」という名前を含むJSONオブジェクトで応答する単純なAPIルートを定義します。このAPIルートにアクセスするには、Next.js開発サーバーを起動します。
npm run dev
その後、ブラウザを開き、http://localhost:3000/api/hello
にアクセスします。以下のJSONレスポンスが表示されるはずです。
{"name": "John Doe"}
APIルートハンドラーを理解する
APIルートのhandler
関数は2つの引数を受け取ります。
req
:http.IncomingMessage
のインスタンスで、リクエストメソッド、ヘッダー、ボディなど、受信リクエストに関する情報が含まれています。res
:http.ServerResponse
のインスタンスで、クライアントに応答を返すことができます。
これらのオブジェクトを使用して、さまざまな種類のリクエストを処理したり、リクエストボディからデータを読み取ったり、レスポンスヘッダーを設定したり、さまざまな種類のレスポンスを送信したりできます。
さまざまなHTTPメソッドの処理
req.method
プロパティを使用して受信リクエストのHTTPメソッドを判断し、それに応じて異なるメソッドを処理できます。例:
// pages/api/method.js
export default function handler(req, res) {
if (req.method === 'GET') {
// GETリクエストを処理
res.status(200).json({ message: 'This is a GET request' })
} else if (req.method === 'POST') {
// POSTリクエストを処理
res.status(200).json({ message: 'This is a POST request' })
} else {
// その他のメソッドを処理
res.status(405).json({ message: 'Method Not Allowed' })
}
}
この例では、APIルートはGETリクエストとPOSTリクエストの両方を処理します。リクエストメソッドがGETの場合、「This is a GET request」というメッセージを含むJSONオブジェクトで応答します。リクエストメソッドがPOSTの場合は、「This is a POST request」というメッセージを含むJSONオブジェクトで応答します。それ以外のリクエストメソッドの場合は、405 Method Not Allowedエラーで応答します。
リクエストボディからのデータ読み取り
POST、PUT、PATCHリクエストでは、多くの場合リクエストボディからデータを読み取る必要があります。Next.jsは、JSONおよびURLエンコードされたリクエストボディの解析を組み込みでサポートしています。JSONリクエストボディを解析するには、req.body
プロパティを使用できます。例:
// pages/api/post.js
export default async function handler(req, res) {
if (req.method === 'POST') {
const { name, email } = req.body
// データを処理
console.log('Name:', name)
console.log('Email:', email)
res.status(200).json({ message: 'Data received successfully' })
} else {
res.status(405).json({ message: 'Method Not Allowed' })
}
}
このAPIルートをテストするには、Postmanやcurlのようなツールを使用して、JSONボディを持つPOSTリクエストを送信できます。
curl -X POST -H "Content-Type: application/json" -d '{"name": "Jane Doe", "email": "jane.doe@example.com"}' http://localhost:3000/api/post
レスポンスヘッダーの設定
res.setHeader()
メソッドを使用してレスポンスヘッダーを設定できます。これは、コンテンツタイプ、キャッシュ制御、その他の重要な情報を設定するのに便利です。例:
// pages/api/headers.js
export default function handler(req, res) {
res.setHeader('Content-Type', 'application/json')
res.setHeader('Cache-Control', 's-maxage=3600')
res.status(200).json({ message: 'Hello, world!' })
}
この例では、APIルートはContent-Type
ヘッダーをapplication/json
に設定し、レスポンスがJSONオブジェクトであることを示します。また、Cache-Control
ヘッダーをs-maxage=3600
に設定し、ブラウザとCDNにレスポンスを最大1時間キャッシュするように指示します。
エラーハンドリング
APIルートではエラーを適切に処理することが重要です。try-catchブロックを使用して例外をキャッチし、クライアントに適切なエラーレスポンスを送信できます。例:
// pages/api/error.js
export default async function handler(req, res) {
try {
// エラーをシミュレート
throw new Error('Something went wrong')
} catch (error) {
console.error(error)
res.status(500).json({ message: 'Internal Server Error' })
}
}
この例では、APIルートは新しいError
オブジェクトをスローすることでエラーをシミュレートします。catchブロックはエラーをキャッチし、コンソールにログを記録し、500 Internal Server Errorレスポンスをクライアントに送信します。本番環境では、SentryやDatadogのような堅牢なロギングシステムの使用を検討してください。
データベースへの接続
APIルートの最も一般的な使用例の1つは、データベースへの接続です。Next.js API Routesは、以下を含むさまざまなデータベースとシームレスに統合できます。
- MongoDB: 柔軟で非構造化データに適した人気のNoSQLデータベース。
- PostgreSQL: 信頼性とデータ整合性で知られる、強力なオープンソースのリレーショナルデータベース。
- MySQL: Webアプリケーションで広く使用されている、もう1つの人気のオープンソースリレーショナルデータベース。
- Firebase: リアルタイムデータベースやその他のサービスを提供するクラウドベースのプラットフォーム。
- FaunaDB: グローバルアプリケーション向けに設計されたサーバーレスデータベース。
以下は、Next.js APIルートでMongoDBデータベースに接続する方法の例です。
// pages/api/mongodb.js
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {}
let client
let clientPromise
if (!process.env.MONGODB_URI) {
throw new Error('Please add your Mongo URI to .env.local')
}
if (process.env.NODE_ENV === 'development') {
// 開発モードでは、HMR(ホットモジュールリプレイスメント)による
// モジュールの再読み込みを跨いで値を維持するためにグローバル変数を使用します。
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// 本番モードでは、グローバル変数を使用しないのが最善です。
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
// モジュールスコープのMongoClientプロミスをエクスポートします。これを別の
// モジュールで行うことで、クライアントは複数の関数で安全に再利用できます。
// 参照: https://github.com/vercel/next.js/blob/canary/examples/with-mongodb/lib/mongodb.js
export default async function handler(req, res) {
try {
const client = await clientPromise
const db = client.db(process.env.MONGODB_DB)
const collection = db.collection('users')
const users = await collection.find({}).toArray()
res.status(200).json({ users })
} catch (e) {
console.error(e)
res.status(500).json({ message: 'Failed to fetch users' })
}
}
このコードを実行する前に、mongodb
パッケージがインストールされていることを確認してください。
npm install mongodb
また、MONGODB_URI
とMONGODB_DB
環境変数を設定する必要もあります。これらの変数は、.env.local
ファイル(または本番環境用のホスティングプロバイダーの環境変数設定)で定義する必要があります。MONGODB_URI
にはMongoDBデータベースへの接続文字列が含まれ、MONGODB_DB
はデータベース名を指定します。
認証と認可
APIルートを保護することは、セキュリティにとって非常に重要です。Next.js API Routesは、以下を含むさまざまな認証および認可技術を使用して保護できます。
- JSON Web Tokens (JWT): パーティ間で情報をJSONオブジェクトとして安全に送信するための標準。
- APIキー: APIエンドポイントへのアクセスを制限する簡単な方法。
- OAuth: ユーザーが自分の認証情報を共有することなく、サードパーティアプリケーションにリソースへのアクセスを許可する委任プロトコル。
- NextAuth.js: Next.jsアプリケーション向けの完全なオープンソース認証ソリューション。
以下は、JWT認証を使用してAPIルートを保護する方法の例です。
// pages/api/protected.js
import jwt from 'jsonwebtoken'
const secret = process.env.JWT_SECRET
export default function handler(req, res) {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401).json({ message: 'Unauthorized' })
}
try {
const decoded = jwt.verify(token, secret)
// "decoded"オブジェクトには、トークンに埋め込まれたユーザー情報が含まれます
// 例: const userId = decoded.userId;
// リクエストの処理を続行
res.status(200).json({ message: 'Protected resource accessed successfully' })
} catch (error) {
return res.status(401).json({ message: 'Invalid token' })
}
}
このコードを実行する前に、jsonwebtoken
パッケージがインストールされていることを確認してください。
npm install jsonwebtoken
また、JWT_SECRET
環境変数を設定する必要もあります。これは、JWTの署名と検証に使用される、強力でランダムに生成された秘密鍵である必要があります。これを安全に保管し、クライアント側のコードで決して公開しないでください。
ミドルウェア
Next.jsはExpress.jsと同じ方法でAPIルート用の伝統的なミドルウェアを提供していませんが、APIルートハンドラーを再利用可能な関数でラップすることで同様の機能を実現できます。これにより、次のようなタスクを実行できます。
- 認証: APIエンドポイントへのアクセスを許可する前にユーザーの資格情報を検証します。
- 認可: ユーザーが特定のアクションを実行するために必要な権限を持っているか確認します。
- ロギング: 監査およびデバッグ目的で受信リクエストと送信レスポンスをログに記録します。
- バリデーション: リクエストデータが特定の基準を満たしていることを検証します。
- レート制限: ユーザーが一定期間内に行えるリクエスト数を制限することで、APIを乱用から保護します。
以下は、簡単なロギングミドルウェアを作成する方法の例です。
// utils/middleware.js
export function withLogging(handler) {
return async function(req, res) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
return handler(req, res)
}
}
このミドルウェアを使用するには、APIルートハンドラーをwithLogging
関数でラップするだけです。
// pages/api/logged.js
import { withLogging } from '../../utils/middleware'
async function handler(req, res) {
res.status(200).json({ message: 'This request was logged' })
}
export default withLogging(handler)
Next.js API Routes構築のベストプラクティス
- APIルートを小さく、焦点を絞ったものに保つ。 各APIルートは特定のタスクまたはリソースを処理する必要があります。
- 機密データには環境変数を使用する。 シークレットやAPIキーをコードにハードコーディングしないでください。
- セキュリティ脆弱性を防ぐためにリクエストデータを検証する。 JoiやYupのようなライブラリを使用してリクエストボディを検証します。
- エラーを適切に処理し、有益なエラーメッセージを提供する。 try-catchブロックを使用し、エラーを中央の場所にログ記録します。
- キャッシュを使用してパフォーマンスを向上させる。 頻繁にアクセスされるデータをキャッシュして、データベースの負荷を軽減します。
- APIルートのパフォーマンスとエラーを監視する。 SentryやDatadogのような監視ツールを使用してAPIの状態を追跡します。
- SwaggerやOpenAPIのようなツールを使用してAPIルートを文書化する。 これにより、他の開発者があなたのAPIを使いやすくなります。
- 型安全のためにTypeScriptの使用を検討する。 TypeScriptは、エラーを早期に発見し、コードの保守性を向上させるのに役立ちます。
- 最初から国際化(i18n)を考慮する。 アプリケーションがさまざまな国のユーザーによって使用される場合、APIルートを複数の言語と通貨をサポートするように設計します。たとえば、eコマースのAPIエンドポイントは、ユーザーの場所に基づいて異なる税率や送料を処理する必要があるかもしれません。
- 適切なCORS(Cross-Origin Resource Sharing)設定を実装する。 これは、APIがNext.jsアプリケーションとは異なるドメインからアクセスされる場合に非常に重要です。承認されたオリジンのみがAPIリソースにアクセスできるようにCORSを慎重に設定します。
高度なテクニック
バックグラウンドジョブ
APIレスポンスをブロックすべきでない長時間実行タスクには、バックグラウンドジョブの使用を検討してください。BullMQやBreeのようなライブラリを使用してバックグラウンドジョブを管理し、非同期に処理できます。
WebSockets
リアルタイムアプリケーションでは、Next.js APIルートでWebSocketを使用できます。Socket.IOやwsのようなライブラリを使用すると、クライアントとサーバー間の持続的な接続を簡単に確立できます。
GraphQL
より柔軟で効率的なデータ取得方法が必要な場合は、GraphQLの使用を検討してください。Apollo ServerやYogaのようなライブラリを使用して、Next.jsアプリケーションにGraphQL APIエンドポイントを作成できます。
結論
Next.js API Routesは、Next.jsアプリケーション内に直接サーバーレスバックエンドを構築するための強力で便利な方法を提供します。サーバーレスアーキテクチャの利点を活用することで、開発を簡素化し、パフォーマンスを向上させ、コストを削減できます。単純な問い合わせフォームの構築から複雑なeコマースプラットフォームの構築まで、Next.js API Routesは堅牢でスケーラブルなバックエンドを簡単に作成するのに役立ちます。基本をしっかりと理解し、ベストプラクティスを適用することで、この強力なツールを活用して、効率的で安全、かつグローバルにアクセス可能なアプリケーションを作成できます。