🕵️‍♂️

Next.js API Routesのreq.bodyの仕様が分からなかったのでコード眺めた

2022/01/30に公開

今回は軽い記事です。Next.js API Routes の req.body って何が返ってくるんでしょう。ドキュメント読んでも、

req.body - An object containing the body parsed by content-type, or null if no body was sent

https://nextjs.org/docs/api-routes/api-middlewares

としか書いてません。こまった。調べてみます。


API Routesのデータは packages/next/server/next-server.tsrunApi() あたりで受けてそうです。

そこから処理を受ける apiResolver() をのぞくと、 bodyParser というキーワードが目に入ってきたので眺めます。


src: next/server/api-utils.ts

この parseBody() を読んでみたら良さそうです。以下に該当箇所の抜粋を貼っておきます。

next/server/api-utils.ts
/**
 * Parse incoming message like `json` or `urlencoded`
 * @param req request object
 */
export async function parseBody(
  req: IncomingMessage,
  limit: string | number
): Promise<any> {
  let contentType
  try {
    contentType = parse(req.headers['content-type'] || 'text/plain')
  } catch {
    contentType = parse('text/plain')
  }
  const { type, parameters } = contentType
  const encoding = parameters.charset || 'utf-8'

  let buffer

  try {
    const getRawBody =
      require('next/dist/compiled/raw-body') as typeof import('next/dist/compiled/raw-body')
    buffer = await getRawBody(req, { encoding, limit })
  } catch (e) {
    if (isError(e) && e.type === 'entity.too.large') {
      throw new ApiError(413, `Body exceeded ${limit} limit`)
    } else {
      throw new ApiError(400, 'Invalid body')
    }
  }

  const body = buffer.toString()

  if (type === 'application/json' || type === 'application/ld+json') {
    return parseJson(body)
  } else if (type === 'application/x-www-form-urlencoded') {
    const qs = require('querystring')
    return qs.decode(body)
  } else {
    return body
  }
}

まとめ

  • 基本的には text/plain として読み込み
    • bufferは config.api.bodyParser.sizeLimit で指定した値か、デフォルトの 1mb
    • charsetは基本的に utf-8 だが、 Content-Type に指定された charset があればそれで読み込む
  • Content-Typeapplication/jsonapplication/ld+json の場合
    • JSON.parse() で読み込み
  • Content-Typeapplication/x-www-form-urlencoded の場合
    • querystring パッケージの querystring.decode() で読み込み

Discussion