日本語

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を使用するメリット

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つの引数を受け取ります。

これらのオブジェクトを使用して、さまざまな種類のリクエストを処理したり、リクエストボディからデータを読み取ったり、レスポンスヘッダーを設定したり、さまざまな種類のレスポンスを送信したりできます。

さまざまな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は、以下を含むさまざまなデータベースとシームレスに統合できます。

以下は、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_URIMONGODB_DB環境変数を設定する必要もあります。これらの変数は、.env.localファイル(または本番環境用のホスティングプロバイダーの環境変数設定)で定義する必要があります。MONGODB_URIにはMongoDBデータベースへの接続文字列が含まれ、MONGODB_DBはデータベース名を指定します。

認証と認可

APIルートを保護することは、セキュリティにとって非常に重要です。Next.js API Routesは、以下を含むさまざまな認証および認可技術を使用して保護できます。

以下は、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ルートハンドラーを再利用可能な関数でラップすることで同様の機能を実現できます。これにより、次のようなタスクを実行できます。

以下は、簡単なロギングミドルウェアを作成する方法の例です。

// 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レスポンスをブロックすべきでない長時間実行タスクには、バックグラウンドジョブの使用を検討してください。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は堅牢でスケーラブルなバックエンドを簡単に作成するのに役立ちます。基本をしっかりと理解し、ベストプラクティスを適用することで、この強力なツールを活用して、効率的で安全、かつグローバルにアクセス可能なアプリケーションを作成できます。