Fastify入門|リクエスト

概要

Fastify は、リクエスト情報の取得や処理を行うためのさまざまなオプションを提供しています。これらのオプションをうまく使うことで、高速かつ柔軟なリクエスト処理が可能となります。

環境

Fastify v4.0.0
Node v19.7.0

リクエスト情報の取得方法

Fastify では、リクエスト情報を取得するために以下のオブジェクトを提供しています。

  • body: POST や PUT メソッドで送信されたリクエストボディのデータ
  • query: クエリストリングのデータ
  • params: パスパラメータのデータ
  • headers: リクエストヘッダーのデータ
  • raw: 生のリクエスト情報を含むオブジェクト
  • server: Fastify インスタンス
  • id: リクエスト ID
  • ip: クライアントの IP アドレス
  • ips: クライアントの IP アドレスの配列
  • hostname: ホスト名
  • protocol: プロトコル
  • url: リクエスト URL
  • routerMethod: リクエストに対するメソッド名
  • routerPath: リクエストに対するパス

これらのオブジェクトを使用することで、リクエスト処理に必要な情報を簡単に取得できます。

また、Fastify はリクエスト処理に必要なさまざまなオプションを提供しています。例えば、bodyLimit オプションは、受け取ることができるリクエストボディの最大サイズを制限することができます。logger オプションは、Pino というロガーライブラリを使用して、アプリケーションのログ出力を行うことができます。

Fastify を使ってリクエスト処理を行う場合、これらのオプションを上手に活用することで、高速かつ柔軟なリクエスト処理が可能となります。

利用例

body

HTTP の POST メソッドや PUT メソッドなどで送信されたデータを取得するためのオブジェクトです。Fastify は、リクエストボディが JSON やフォームデータの場合に自動的にパースします。パースされたデータは、body プロパティにオブジェクトとして格納されます。以下は、POST リクエストで送信されたデータを取得する例です。

fastify.post('/submit', (request, reply) => {
  const data = request.body
  // リクエストボディのデータを処理する
})

request.body オブジェクトは、multipart/form-data 形式のリクエストボディを受け取る際にも使用されます。ただし、Fastify では、multipart/form-data の処理に busboy というライブラリを使用しています。従って、request.body オブジェクトが使用可能になる前に、リクエストストリームの読み込みが完了している必要があります。そのため、body パース処理中にエラーが発生する可能性があることに注意してください。

query

Fastify において、query は HTTP リクエストのクエリストリングを表すオブジェクトです。クエリストリングとは、URL の末尾に付与されるパラメータを表現するために使われます。

例えば、以下の URL の場合、クエリストリングは「q=test」です。

https://example.com/search?q=test

Fastify の場合、クエリストリングはリクエストオブジェクトの query プロパティで参照できます。例えば、以下のようなハンドラー関数では、クエリストリングの q パラメータの値を取得しています。

fastify.get('/search', function (request, reply) {
  const query = request.query
  const searchTerm = query.q
  reply.send({ message: `Search term is: ${searchTerm}` })
})

この例では、リクエストオブジェクトの query プロパティには、クエリストリングのキーと値がキャッシュされたオブジェクトが含まれます。リクエストオブジェクトの query プロパティには、クエリストリングのキーに対応する値を取得するために、オブジェクトのプロパティアクセス構文を使用してアクセスすることができます。

また、Fastify ではクエリストリングのバリデーションを実行することもできます。バリデーションには、Fastify が提供する JSON スキーマ検証ツールである Ajv を使用することができます。以下は、クエリストリングに対する Ajv の使用例です。

const schema = {
  type: 'object',
  properties: {
    q: { type: 'string', minLength: 1 },
    limit: { type: 'integer', minimum: 1, maximum: 100 },
  },
  required: ['q'],
}

fastify.get('/search', {
  schema: {
    querystring: schema,
  },
  handler: async function (request, reply) {
    const query = request.query
    const searchTerm = query.q
    const limit = query.limit || 10
    reply.send({ message: `Search term is: ${searchTerm}, limit is ${limit}` })
  }
})

この例では、querystring プロパティに JSON スキーマを指定しています。これにより、Fastify はクエリストリングのバリデーションを実行し、指定されたスキーマに一致しない場合にはエラーを返します。バリデーションが成功すると、リクエストオブジェクトの query プロパティには、スキーマに合致したキーと値が含まれます。

params

Fastify では、パスの一部に含まれるパラメータを取得するために params オブジェクトを提供しています。パラメータは、コロン(:)で始まり、次にパラメータ名が続く形式でパスに埋め込まれます。たとえば、次のようなパスがあった場合、id と category の 2 つのパラメータが取得できます。

/items/:id/category/:category

params オブジェクトには、パラメータ名をキーにして、各パラメータに対応する値が格納されます。以下は、params オブジェクトを使って、id と category パラメータの値を取得する例です。

fastify.get('/items/:id/category/:category', function (request, reply) {
  const itemId = request.params.id
  const category = request.params.category
  reply.send(`Item ${itemId} belongs to category ${category}`)
})

リクエストが/items/123/category/books であった場合、itemId には 123 が、category には books が格納されます。

headers

headers は、HTTP リクエストのヘッダー情報を含むオブジェクトです。例えば、User-Agent や Accept-Language などのヘッダーフィールドの値が含まれます。

Fastify では、headers オブジェクトを使用して HTTP リクエストのヘッダー情報を簡単に取得することができます。以下は、headers オブジェクトを使用して、HTTP リクエストの Accept-Language ヘッダーフィールドの値を取得する例です。

fastify.get('/example', (req, res) => {
  const acceptLanguage = req.headers['accept-language'];
  res.send(`Accept-Language: ${acceptLanguage}`);
});

headers オブジェクトは、キーと値のペアの形式でヘッダーフィールドの情報を格納します。キーは、ヘッダーフィールドの名前を表し、値は、ヘッダーフィールドの値を表します。

注意点として、HTTP リクエストのヘッダー情報は、大文字と小文字を区別しないで扱われます。つまり、headers オブジェクトのキーはすべて小文字で表記されます。

raw

raw オブジェクトは、生の HTTP リクエスト情報を含むオブジェクトです。このオブジェクトには、リクエストのパス、ヘッダー、クエリストリング、ボディなどが含まれます。

raw オブジェクトを使用すると、リクエスト情報を自由に操作できるため、Fastify の高い柔軟性を活かすことができます。ただし、raw オブジェクトを使う場合は、セキュリティ上の問題に注意する必要があります。

以下は、raw オブジェクトを使用してリクエストヘッダーを取得する例です。

fastify.get('/headers', function (request, reply) {
  const headers = request.raw.headers;
  reply.send(headers);
});

この例では、リクエストヘッダーを取得してレスポンスとして返しています。raw オブジェクトを使用することで、リクエスト情報を柔軟に操作することができます。ただし、セキュリティ上の問題に注意しなければなりません。

server

Fastify の server オブジェクトは、Fastify アプリケーションがバインドされている HTTP サーバーインスタンスを表します。このオブジェクトを介して、さまざまな HTTP サーバーイベントに対してリッスンしたり、HTTP サーバーオプションを設定したりできます。

例えば、以下のように Fastify アプリケーションを作成し、server オブジェクトを介して HTTP サーバーイベントをリッスンできます。

const fastify = require('fastify')()

fastify.get('/', function (req, reply) {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, function (err, address) {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  console.log(`Server listening on ${address}`)
})

fastify.server.on('error', function (err) {
  console.error(`Server error: ${err}`)
})

この例では、fastify.listen()メソッドで Fastify アプリケーションを 3000 番ポートで起動し、server オブジェクトの on()メソッドを使って、エラーイベントをリッスンしています。

id

Fastify はリクエストごとに固有の ID を生成し、そのリクエストに関するログ出力に使用するために id プロパティを提供しています。

例えば、Fastify のデフォルトのロガーを使用する場合、リクエストごとに ID が生成され、ログメッセージに出力されます。以下は、Fastify のデフォルトのロガーを使用する例です。

const fastify = require('fastify')({ logger: true })

fastify.get('/', (req, reply) => {
  req.log.info('Hello, World!')
  reply.send({ hello: 'world' })
})

fastify.listen(3000)

この場合、リクエストごとに固有の ID が生成され、ログメッセージに出力されます。

{"level":30,"time":1616499122217,"pid":1234,"hostname":"example.com","reqId":"b1d9e3e3-8f17-45c3-966a-ec921013dd01","msg":"incoming request","method":"GET","url":"/","query":{},"headers":{"host":"example.com:3000","user-agent":"curl/7.54.0","accept":"*/*"},"remoteAddress":"127.0.0.1","remotePort":51078}
{"level":30,"time":1616499122218,"pid":1234,"hostname":"example.com","reqId":"b1d9e3e3-8f17-45c3-966a-ec921013dd01","msg":"request completed","statusCode":200,"body":{"hello":"world"}}

id プロパティは、リクエスト全体のライフサイクルを通じて一意の ID を提供するため、デバッグやトレースのためのツールとして役立ちます。

ip

ip は、リクエストを送信しているクライアントの IP アドレスを表す Fastify のリクエストオブジェクトのプロパティです。IP アドレスを取得するためには、以下のようにして request.ip にアクセスします。

const fastify = require('fastify')()

fastify.get('/', (request, reply) => {
  const clientIP = request.ip
  reply.send(`Your IP address is: ${clientIP}`)
})

Fastify は request.ip プロパティを自動的に設定するため、IP アドレスを手動で取得する必要はありません。ただし、Fastify はプロキシサーバー経由でリクエストが送信された場合、リクエストヘッダーに含まれる X-Forwarded-For ヘッダーからクライアントの IP アドレスを取得するため、 request.ip プロパティにプロキシサーバーの IP アドレスが含まれることがあります。

このような場合、正しいクライアント IP アドレスを取得するためには、trustProxy オプションを使用して、信頼できるプロキシサーバーを設定する必要があります。 trustProxy オプションを設定するには、以下のようにします。

const fastify = require('fastify')({
  trustProxy: '192.168.1.1'
})

fastify.get('/', (request, reply) => {
  const clientIP = request.ip
  reply.send(`Your IP address is: ${clientIP}`)
})

trustProxy オプションを使用すると、Fastify は X-Forwarded-For ヘッダーからクライアント IP アドレスを正しく取得することができます。ただし、このオプションを使用する場合、悪意のある攻撃者が X-Forwarded-For ヘッダーを偽装することに注意する必要があります。

ips

ips は、リクエストが経由した IP アドレスのリストを表します。

一般的に、クライアントからのリクエストを処理する際に、複数の中継サーバーを経由することがあります。この場合、リクエストの ip プロパティには、中継サーバーの IP アドレスが格納されます。一方、ips プロパティには、リクエストが経由したすべての IP アドレスが格納されます。

例えば、リクエストが以下のように経由した場合、

クライアント -> 中継サーバー 1 -> 中継サーバー 2 -> アプリケーションサーバー

ips プロパティには、以下のように IP アドレスのリストが格納されます。

const fastify = require('fastify')()

fastify.get('/', function (req, reply) {
  const clientIPs =  req.ips
  reply.send(`clientIPs is ${clientIPs}`)
})

fastify.listen(3000, function (err, address) {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  console.log(`Server listening at ${address}`)
})
[  'クライアントのIPアドレス',  '中継サーバー1のIPアドレス',  '中継サーバー2のIPアドレス']

注意点として、ips プロパティは、リクエストが経由したすべての IP アドレスをリストとして格納するため、IP アドレスが改ざんされた場合には信用できなくなります。そのため、セキュリティ上の理由から、IP アドレスの検証には ip プロパティを使用することを推奨します。

hostname

hostname は、Fastify によって処理されたリクエストのホスト名を示すプロパティです。hostname プロパティは、リクエストを送信したユーザーのマシンのホスト名を示します。ホスト名は、IP アドレスに関連付けられた識別子で、DNS によって解決されます。

hostname プロパティは、リクエストの headers オブジェクトから自動的に抽出されます。headers オブジェクトに host ヘッダーが含まれている場合、hostname プロパティには host ヘッダーの値が格納されます。host ヘッダーが存在しない場合、hostname プロパティには空の文字列が格納されます。

以下は、hostname プロパティを使った例です。

const fastify = require('fastify')()

fastify.get('/', function (req, reply) {
  const hostname = req.hostname
  reply.send(`Your hostname is ${hostname}`)
})

fastify.listen(3000, function (err, address) {
  if (err) {
    console.log(err)
    process.exit(1)
  }
  console.log(`Server listening on ${address}`)
})

この例では、クライアントが GET リクエストを送信すると、Fastify は hostname プロパティを使用して、リクエストのホスト名を返します。これにより、リクエスト元のマシンのホスト名を確認できます。

protocol

Fastify における protocol は、クライアントとサーバー間で使用されているプロトコルを表します。たとえば、HTTP の場合、protocol は http となります。HTTPS を使用している場合、protocol は https になります。

protocol は、リクエストハンドラー内で以下のように取得できます。

fastify.get('/', (request, reply) => {
  const protocol = request.protocol; // 'http' or 'https'
  reply.send(`Protocol used: ${protocol}`);
});

Fastify は、HTTP/1.1 および HTTP/2 プロトコルの両方をサポートしています。HTTP/1.1 を使用する場合、デフォルトのポート番号は 80 で、HTTPS を使用する場合は 443 です。HTTP/2 を使用する場合、デフォルトのポート番号は 443 です。

Fastify を使用する場合、通常は HTTP/2 を使用することをお勧めします。HTTP/2 は、HTTP/1.1 よりも高速で、より多くのリクエストを処理できます。Fastify は、HTTP/2 を使用するために、内部的に http2 モジュールを使用しています。

url

url はリクエストされた URL に関する情報を含むオブジェクトで、以下のプロパティが含まれます。

  1. href: リクエストされた URL 全体の文字列
  2. pathname: リクエストされた URL のパス部分
  3. query: リクエストされた URL のクエリストリングのオブジェクト表現
  4. search: リクエストされた URL のクエリストリングの文字列表現

url オブジェクトを使用することで、リクエスト URL に関する情報を簡単に取得することができます。

以下は、url オブジェクトを使って、リクエストされた URL のパス部分を取得する例です。

const fastify = require('fastify')()

fastify.get('/hello', function (request, reply) {
  const path = request.url.pathname
  reply.send(`Hello, path is ${path}`)
})

fastify.listen(3000, function (err) {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  console.log(`server listening on ${fastify.server.address().port}`)
})

上記の例では、request.url.pathname を使用して、リクエストされた URL のパス部分を取得しています。request.url オブジェクトは、Fastify でリクエストされた URL の情報を取得する際に便利なオブジェクトの一つです。

routerPath

routerMethod は、Fastify のルーターによって使用されるメソッド名を取得するためのプロパティです。

Fastify では、ルーターを使用して URL パスに対して HTTP メソッドを割り当てることができます。たとえば、GET メソッドで /users パスにアクセスする場合、以下のように処理されます。

fastify.get('/users', (req, res) => {
  // do something
});

この場合、routerMethod プロパティには “GET” が格納されます。

routerMethod プロパティは、preHandler フックや、カスタムエラーハンドラーで使用されることがあります。たとえば、特定の HTTP メソッドに対してカスタムのエラーレスポンスを返すように指定することができます。

まとめ

Fastify を使用することで、様々なリクエスト処理を柔軟かつ高速に行うことができます。Fastify では、リクエスト情報を取得するためのさまざまなオブジェクトを提供しており、これらを使うことで、簡単にリクエスト処理に必要な情報を取得することができます。