コンテンツにスキップ

APIルート

豆知識: App Routerを使用している場合は、API Routesの代わりにServer ComponentsまたはRoute Handlersを使用できます。

API routesは、Next.jsで公開APIを構築するためのソリューションを提供します。

pages/apiフォルダー内のファイルは/api/*にマッピングされ、pageではなくAPIエンドポイントとして扱われます。これらはサーバーサイドのみのバンドルであり、クライアントサイドのバンドルサイズを増やすことはありません。

たとえば、次のAPIルートは200のステータスコードでJSONレスポンスを返します。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

知っておくと良いこと:

  • API Routesでは、デフォルトでCORSヘッダーが指定されないため、同一オリジンのみとなります。CORSリクエストヘルパーでこの動作をカスタマイズできます。

Parameters

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // ...
}

HTTPメソッド

APIルートで異なるHTTPメソッドを処理するには、リクエストハンドラーでreq.methodを使用します。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    // Process a POST request
  } else {
    // Handle any other HTTP method
  }
}

リクエストヘルパー

API Routesは、受信リクエスト(req)を解析する組み込みのリクエストヘルパーを提供します。

  • req.cookies - リクエストで送信されたCookieを含むオブジェクト。デフォルトは{}です。
  • req.query - クエリ文字列を含むオブジェクト。デフォルトは{}です。
  • req.body - content-typeによって解析されたボディを含むオブジェクト。ボディが送信されなかった場合はnullです。

カスタム設定

各API Routeは、デフォルト設定を変更するためにconfigオブジェクトをエクスポートできます。デフォルト設定は次のとおりです。

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
  // Specifies the maximum allowed duration for this function to execute (in seconds)
  maxDuration: 5,
}

bodyParserは自動的に有効になっています。ボディをStreamとして、またはraw-bodyで消費したい場合は、これをfalseに設定できます。

自動bodyParsingを無効にするユースケースの1つは、たとえばGitHubからのWebhookリクエストの生のボディを検証できるようにすることです。

export const config = {
  api: {
    bodyParser: false,
  },
}

bodyParser.sizeLimitは、bytesでサポートされている任意の形式で、解析されたボディに許可される最大サイズです。

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '500kb',
    },
  },
}

externalResolverは、このルートがexpressまたはconnectのような外部リゾルバによって処理されていることをサーバーに伝える明示的なフラグです。このオプションを有効にすると、未解決のリクエストに関する警告が無効になります。

export const config = {
  api: {
    externalResolver: true,
  },
}

responseLimitは自動的に有効になり、API Routesのレスポンスボディが4MBを超える場合に警告が表示されます。

Next.jsをサーバーレス環境で使用しておらず、CDNまたは専用メディアホストを使用しないことによるパフォーマンスへの影響を理解している場合は、この制限をfalseに設定できます。

export const config = {
  api: {
    responseLimit: false,
  },
}

responseLimitには、バイト数またはbytesでサポートされている任意の文字列形式(例: 1000'500kb''3mb')を指定することもできます。この値は、警告が表示される前の最大レスポンスサイズになります。デフォルトは4MBです。(上記参照)

export const config = {
  api: {
    responseLimit: '8mb',
  },
}

レスポンスヘルパー

res(しばしばresと省略される)Server Responseオブジェクトには、開発者エクスペリエンスを向上させ、新しいAPIエンドポイントの作成速度を向上させるためのExpress.jsライクなヘルパーメソッドのセットが含まれています。

含まれるヘルパーは次のとおりです。

  • res.status(code) - ステータスコードを設定する関数。codeは有効なHTTPステータスコードである必要があります。
  • res.json(body) - JSONレスポンスを送信します。bodyシリアライズ可能なオブジェクトである必要があります。
  • res.send(body) - HTTPレスポンスを送信します。bodystringobject、またはBufferにすることができます。
  • res.redirect([status,] path) - 指定されたパスまたはURLにリダイレクトします。statusは有効なHTTPステータスコードである必要があります。指定しない場合、statusはデフォルトで「307」「Temporary redirect」になります。
  • res.revalidate(urlPath) - getStaticPropsを使用して、オンデマンドでページを再検証します。urlPathstringである必要があります。

レスポンスのステータスコードの設定

クライアントにレスポンスを送信する際に、レスポンスのステータスコードを設定できます。

次の例は、レスポンスのステータスコードを200OK)に設定し、JSONレスポンスとしてmessageプロパティにHello from Next.js!という値を持つものを返します。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

JSONレスポンスの送信

クライアントにレスポンスを送信する際にJSONレスポンスを送信できます。これはシリアライズ可能なオブジェクトである必要があります。実際のアプリケーションでは、リクエストされたエンドポイントの結果に応じて、クライアントにリクエストのステータスを知らせたい場合があります。

次の例は、ステータスコード200OK)と非同期操作の結果を持つJSONレスポンスを送信します。エラーが発生した場合に適切なステータスコードとエラーメッセージをキャッチしてクライアントに返すために、try-catchブロック内に含まれています。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: 'failed to load data' })
  }
}

HTTPレスポンスの送信

HTTPレスポンスの送信は、JSONレスポンスの送信と同じように機能します。唯一の違いは、レスポンスボディがstringobject、またはBufferのいずれかであることです。

次の例は、ステータスコード200OK)と非同期操作の結果を持つHTTPレスポンスを送信します。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: 'failed to fetch data' })
  }
}

指定されたパスまたはURLへのリダイレクト

フォームを例にとると、フォーム送信後にクライアントを指定されたパスまたはURLにリダイレクトさせたい場合があります。

次の例では、フォームが正常に送信された場合にクライアントを/パスにリダイレクトさせます。

pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { name, message } = req.body
 
  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: 'Failed to fetch data' })
  }
}

TypeScriptの型を追加する

nextからNextApiRequestNextApiResponse型をインポートすることで、API Routesをより型安全にすることができます。それに加えて、レスポンスデータにも型を付けることができます。

import type { NextApiRequest, NextApiResponse } from 'next'
 
type ResponseData = {
  message: string
}
 
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

豆知識: NextApiRequestのボディはanyです。なぜなら、クライアントはいかなるペイロードでも含める可能性があるからです。使用する前に、実行時にボディの型/形状を検証する必要があります。

動的なAPI Routes

API Routesは、pages/で使用されるファイル命名規則と同じ規則を使用して、動的ルートをサポートします。

pages/api/post/[pid].ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

これにより、/api/post/abcへのリクエストは、Post: abcというテキストで応答します。

Catch all API routes

API Routesは、角括弧(...)内に3つのドットを追加することで、すべてのパスをキャッチするように拡張できます。たとえば、

  • pages/api/post/[...slug].js/api/post/aだけでなく、/api/post/a/b/api/post/a/b/cなどもマッチします。

豆知識: slug以外の名前(例: [...param])も使用できます。

マッチしたパラメータは、ページにクエリパラメータ(例ではslug)として送信され、常に配列になります。したがって、パス/api/post/aは次のqueryオブジェクトを持ちます。

{ "slug": ["a"] }

/api/post/a/bの場合、および他のマッチするパスの場合、配列に新しいパラメータが追加されます。

{ "slug": ["a", "b"] }

例:

pages/api/post/[...slug].ts
import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}

これにより、/api/post/a/b/cへのリクエストは、Post: a, b, cというテキストで応答します。

Optional catch all API routes

Catch all routesは、パラメータを二重角括弧([[...slug]])に含めることでオプションにすることができます。

たとえば、pages/api/post/[[...slug]].js/api/post/api/post/a/api/post/a/bなどにマッチします。

Catch all routesとOptional catch all routesの主な違いは、Optionalではパラメータのないルートもマッチすることです(上記の例では/api/post)。

queryオブジェクトは次のとおりです。

{ } // GET `/api/post` (empty object)
{ "slug": ["a"] } // `GET /api/post/a` (single-element array)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (multi-element array)

注意点

  • 事前定義されたAPI Routesは動的API Routesよりも優先され、動的API RoutesはCatch all API Routesよりも優先されます。次の例をご覧ください。
    • pages/api/post/create.js - /api/post/createにマッチします。
    • pages/api/post/[pid].js - /api/post/1/api/post/abcなどにマッチします。ただし、/api/post/createにはマッチしません。
    • pages/api/post/[...slug].js - /api/post/1/2/api/post/a/b/cなどにマッチします。ただし、/api/post/create/api/post/abcにはマッチしません。

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

Pages RouterはAPI Routesでのストリーミングレスポンスをサポートしていますが、App Routerへの段階的な移行を推奨しており、Next.js 14+の場合はRoute Handlersの使用を推奨します。

writeHeadを使用してAPI Routeからレスポンスをストリーミングする方法は次のとおりです。

pages/api/hello.ts
import { NextApiRequest, NextApiResponse } from 'next'
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-store',
  })
  let i = 0
  while (i < 10) {
    res.write(`data: ${i}\n\n`)
    i++
    await new Promise((resolve) => setTimeout(resolve, 1000))
  }
  res.end()
}