本文へスキップ
バージョン: 最新版 (v5.0.x)

ルート

ルート

ルートメソッドはアプリケーションのエンドポイントを設定します。Fastifyでは、簡略メソッドと完全宣言の2つの方法でルートを宣言できます。

完全宣言

fastify.route(options)

ルートオプション

  • method: 現在、GETHEADTRACEDELETEOPTIONSPATCHPUTPOSTをサポートしています。さらに多くのメソッドを受け入れるには、addHttpMethod を使用する必要があります。メソッドの配列にすることもできます。

  • url: このルートに一致するURLのパス(別名: path)。

  • schema: リクエストとレスポンスのスキーマを含むオブジェクト。 JSON スキーマ 形式にする必要があります。詳細は こちら を参照してください。

    • body: POST、PUT、PATCH、TRACE、SEARCH、PROPFIND、PROPPATCH、またはLOCKメソッドの場合、リクエストの本文を検証します。
    • querystringまたはquery: クエリ文字列を検証します。これは、typeプロパティがobjectで、propertiesオブジェクトがパラメータの完全なJSONスキーマオブジェクト、または下記のようにpropertiesオブジェクトに含まれる値を単純に指定できます。
    • params: パラメータを検証します。
    • response: レスポンスのスキーマをフィルタリングして生成します。スキーマを設定することで、スループットを10~20%向上させることができます。
  • exposeHeadRoute: すべてのGETルートに対して、兄弟のHEADルートを作成します。exposeHeadRoutes インスタンスオプションの値をデフォルトで使用します。このオプションを無効にせずにカスタムHEADハンドラーが必要な場合は、GETルートの前に定義してください。

  • attachValidation: スキーマ検証エラーがある場合、エラーをエラーハンドラーに送信する代わりに、validationErrorをリクエストにアタッチします。デフォルトのエラー形式はAjvの形式です。

  • onRequest(request, reply, done): リクエストを受信するとすぐに呼び出される関数。関数の配列にすることもできます。

  • preParsing(request, reply, done): リクエストの解析前に呼び出される関数。関数の配列にすることもできます。

  • preValidation(request, reply, done): 共通のpreValidationフックの後で呼び出される関数。例えば、ルートレベルで認証を実行する必要がある場合に役立ちます。関数の配列にすることもできます。

  • preHandler(request, reply, done): リクエストハンドラーの直前に呼び出される関数。関数の配列にすることもできます。

  • preSerialization(request, reply, payload, done): シリアライゼーションの直前に呼び出される関数。関数の配列にすることもできます。

  • onSend(request, reply, payload, done): レスポンスの送信直前に呼び出される関数。関数の配列にすることもできます。

  • onResponse(request, reply, done): レスポンスが送信されたときに呼び出される関数。クライアントにさらにデータを送信することはできません。関数の配列にすることもできます。

  • onTimeout(request, reply, done): リクエストのタイムアウトが発生し、HTTPソケットが切断されたときに呼び出される関数

  • onError(request, reply, error, done): ルートハンドラーによってエラーがスローまたはクライアントに送信されたときに呼び出される関数

  • handler(request, reply): このリクエストを処理する関数。Fastifyサーバーは、ハンドラーが呼び出されるときにthisにバインドされます。注: アロー関数を使用すると、thisのバインドが解除されます。

  • errorHandler(error, request, reply): リクエストのスコープに対するカスタムエラーハンドラー。グローバルなデフォルトエラーハンドラー、およびsetErrorHandlerで設定されたものを、ルートへのリクエストに対して上書きします。デフォルトのハンドラーにアクセスするには、instance.errorHandlerにアクセスできます。これは、プラグインが既に上書きしていない場合にのみ、fastifyのデフォルトのerrorHandlerを指していることに注意してください。

  • childLoggerFactory(logger, binding, opts, rawReq): すべてのリクエストに対して子ロガーインスタンスを作成するために呼び出されるカスタムファクトリー関数。childLoggerFactoryの詳細を参照してください。setChildLoggerFactoryで設定されたものも含め、デフォルトのロガーファクトリーをルートへのリクエストに対して上書きします。デフォルトのファクトリーにアクセスするには、instance.childLoggerFactoryにアクセスできます。これは、プラグインが既に上書きしていない場合にのみ、FastifyのデフォルトのchildLoggerFactoryを指していることに注意してください。

  • validatorCompiler({ schema, method, url, httpPart }): リクエストのバリデーションのためのスキーマを構築する関数。バリデーションとシリアライゼーションドキュメントを参照してください。

  • serializerCompiler({ { schema, method, url, httpStatus, contentType } }): レスポンスのシリアライゼーションのためのスキーマを構築する関数。バリデーションとシリアライゼーションドキュメントを参照してください。

  • schemaErrorFormatter(errors, dataVar): バリデーションコンパイラからのエラーをフォーマットする関数。バリデーションとシリアライゼーションドキュメントを参照してください。グローバルなスキーマエラーフォーマッターハンドラー、およびsetSchemaErrorFormatterで設定されたものを、ルートへのリクエストに対して上書きします。

  • bodyLimit: デフォルトのJSON本文パーサーが、このバイト数よりも大きいリクエスト本文を解析するのを防ぎます。整数でなければなりません。fastify(options)でFastifyインスタンスを最初に作成するときに、このオプションをグローバルに設定することもできます。デフォルトは1048576(1 MiB)です。

  • logLevel: このルートのログレベルを設定します。下記を参照してください。

  • logSerializers: このルートのログにシリアライザーを設定します。

  • config: カスタム設定を格納するために使用されるオブジェクト。

  • version: エンドポイントのバージョンを定義するsemver互換の文字列。.

  • constraints: リクエストのプロパティまたは値に基づいてルートの制限を定義し、find-my-wayの制約を使用してカスタマイズされたマッチングを有効にします。組み込みのversionおよびhost制約を含み、カスタム制約戦略をサポートしています。

  • prefixTrailingSlash: プレフィックス付きルートとして/を扱う方法を決定するために使用される文字列。

    • both (デフォルト): /prefix/prefix/の両方を登録します。
    • slash: /prefix/のみを登録します。
    • no-slash: /prefixのみを登録します。

    注: このオプションはサーバー設定のignoreTrailingSlashを上書きしません。

  • requestリクエストで定義されています。

  • replyレスポンスで定義されています。

注意: onRequestpreParsingpreValidationpreHandlerpreSerializationonSendonResponseのドキュメントは、フックでより詳細に説明されています。さらに、リクエストがhandlerによって処理される前にレスポンスを送信するには、フックからのリクエストへの応答を参照してください。

fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
name: { type: 'string' },
excitement: { type: 'integer' }
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})

簡略宣言

上記のルート宣言はよりHapiライクですが、Express/Restifyアプローチを好む場合は、それもサポートしています。

fastify.get(path, [options], handler)

fastify.head(path, [options], handler)

fastify.post(path, [options], handler)

fastify.put(path, [options], handler)

fastify.delete(path, [options], handler)

fastify.options(path, [options], handler)

fastify.patch(path, [options], handler)

const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ hello: 'world' })
})

fastify.all(path, [options], handler)は、サポートされているすべてのメソッドに同じハンドラーを追加します。

ハンドラーはoptionsオブジェクト経由で指定することもできます。

const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)

注: ハンドラーがoptionsとショートカットメソッドの第3パラメーターの両方で指定されている場合、重複したhandlerエラーをスローします。

URL構築

Fastifyは静的URLと動的URLの両方をサポートしています。

パラメトリックなパスを登録するには、パラメーター名の前にコロンを使用します。ワイルドカードには、アスタリスクを使用します。静的ルートは常にパラメトリックルートとワイルドカードルートの前にチェックされます。

// parametric
fastify.get('/example/:userId', function (request, reply) {
// curl ${app-url}/example/12345
// userId === '12345'
const { userId } = request.params;
// your code here
})
fastify.get('/example/:userId/:secretToken', function (request, reply) {
// curl ${app-url}/example/12345/abc.zHi
// userId === '12345'
// secretToken === 'abc.zHi'
const { userId, secretToken } = request.params;
// your code here
})

// wildcard
fastify.get('/example/*', function (request, reply) {})

正規表現ルートもサポートされていますが、スラッシュをエスケープする必要があることに注意してください。正規表現はパフォーマンス面で非常にコストがかかることにも注意してください!

// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) {
// curl ${app-url}/example/12345.png
// file === '12345'
const { file } = request.params;
// your code here
})

同じスラッシュのペア内(" / ")に複数のパラメーターを定義することができます。例えば

fastify.get('/example/near/:lat-:lng/radius/:r', function (request, reply) {
// curl ${app-url}/example/near/15°N-30°E/radius/20
// lat === "15°N"
// lng === "30°E"
// r ==="20"
const { lat, lng, r } = request.params;
// your code here
})

この場合、パラメーターセパレーターとしてダッシュ("-")を使用してください。

最後に、正規表現で複数のパラメーターを持つことができます。

fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, reply) {
// curl ${app-url}/example/at/08h24m
// hour === "08"
// minute === "24"
const { hour, minute } = request.params;
// your code here
})

この場合、パラメーターセパレーターとして、正規表現に一致しない任意の文字を使用できます。

最後のパラメーターは、パラメーター名の最後に疑問符("?")を追加することでオプションにすることができます。

fastify.get('/example/posts/:id?', function (request, reply) {
const { id } = request.params;
// your code here
})

この場合、/example/posts/example/posts/1の両方をリクエストできます。オプションパラメーターは、指定されていない場合は未定義になります。

複数のパラメーターを持つルートはパフォーマンスに悪影響を与える可能性があるため、特にアプリケーションのホットパスにあるルートでは、可能な限り単一パラメーターのアプローチを優先してください。ルーティングの処理方法に興味がある場合は、find-my-wayを参照してください。

パラメーターを宣言せずにコロンを含むパスが必要な場合は、ダブルコロンを使用します。例えば

fastify.post('/name::verb') // will be interpreted as /name:verb

非同期/await

あなたはasync/awaitユーザーですか?大丈夫ですよ!

fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
return processed
})

ご覧のように、ユーザーにデータを送信するためにreply.sendを呼び出していません。本文を返すだけで完了です!

必要であれば、reply.send を使用してユーザーにデータを返送することもできます。この場合、非同期ハンドラーで return reply または await reply を忘れずに行ってください。そうでないと、特定の状況で競合状態が発生する可能性があります。

fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
return reply.send(processed)
})

ルートがコールバックベースのAPIをラップしており、プロミスチェーンの外でreply.send()を呼び出す場合、await replyを使用できます。

fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})

reply を返す方法も有効です。

fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})

警告

  • return valuereply.send(value) を同時に使用する場合、先に実行された方が優先され、2番目の値は破棄されます。また、2回レスポンスを送信しようとしたため、警告ログも出力されます。
  • プロミスの外でreply.send()を呼び出すことは可能ですが、特別な注意が必要です。詳細はプロミス解決を参照してください。
  • undefinedを返すことはできません。詳細はプロミス解決を参照してください。

プロミス解決

ハンドラーが非同期関数であるか、プロミスを返す場合、コールバックとプロミスの制御フローをサポートするために必要な特別な動作を認識しておく必要があります。ハンドラーのプロミスが解決されると、明示的にハンドラー内でreplyをawaitまたはreturnしない限り、その値を使用してレスポンスが自動的に送信されます。

  1. async/awaitまたはプロミスを使用したいが、reply.sendで値をレスポンスとして返したい場合
    • 必ず return reply / await reply を行ってください。
    • reply.send の呼び出しを忘れないでください
  2. async/awaitまたはプロミスを使用したい場合
    • reply.send使用しないでください
    • 送信したい値を返してください

このようにして、最小限のトレードオフでコールバックスタイルasync-awaitの両方をサポートできます。これだけ自由度が高いにも関わらず、エラー処理はアプリケーション内で一貫して処理する必要があるため、1つのスタイルのみを使用することを強くお勧めします。

注意:すべての非同期関数は、それ自体でプロミスを返します。

ルートプレフィックス

同じAPIの2つ以上の異なるバージョンを維持する必要がある場合があります。古典的なアプローチは、すべてのルートにAPIバージョン番号(例:/v1/user)をプレフィックスとして付けることです。Fastifyは、すべてのルート名を手動で変更することなく、同じAPIの異なるバージョンを迅速かつスマートに作成するための方法、ルートプレフィックスを提供します。その仕組みを見てみましょう。

// server.js
const fastify = require('fastify')()

fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })

fastify.listen({ port: 3000 })
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}

コンパイル時にプレフィックスが自動的に処理されるため(これはパフォーマンスにも全く影響しないことを意味します!)、同じ名前を2つの異なるルートで使用しても、Fastifyはエラーを報告しません。

これで、クライアントは次のルートにアクセスできるようになります。

  • /v1/user
  • /v2/user

これは必要な回数だけ実行でき、ネストされたregisterにも機能し、ルートパラメーターもサポートされています。

すべてのルートにプレフィックスを使用したい場合は、プラグイン内に配置できます。

const fastify = require('fastify')()

const route = {
method: 'POST',
url: '/login',
handler: () => {},
schema: {},
}

fastify.register(function (app, _, done) {
app.get('/users', () => {})
app.route(route)

done()
}, { prefix: '/v1' }) // global route prefix

await fastify.listen({ port: 3000 })

ルートプレフィックスとfastify-plugin

fastify-pluginを使用してルートをラップする場合、このオプションは機能しません。プラグインをプラグインでラップすることで機能させることができます。例:

const fp = require('fastify-plugin')
const routes = require('./lib/routes')

module.exports = fp(async function (app, opts) {
app.register(routes, {
prefix: '/v1',
})
}, {
name: 'my-routes'
})

プレフィックス付きプラグイン内の/ルートの処理

/ルートの動作は、プレフィックスが/で終わるかどうかにより異なります。例として、プレフィックスが/something/の場合、/ルートを追加しても/something/のみに一致します。プレフィックスが/somethingの場合、/ルートを追加すると/something/something/の両方に一致します。

この動作を変更するには、上記のprefixTrailingSlashルートオプションを参照してください。

カスタムログレベル

ルートで異なるログレベルが必要になる場合があります。Fastifyでは、非常に簡単な方法でこれを実現できます。

プラグインオプションまたはルートオプションにlogLevelオプションと必要なを渡すだけです。

プラグインレベルでlogLevelを設定すると、setNotFoundHandlersetErrorHandlerも影響を受けることに注意してください。

// server.js
const fastify = require('fastify')({ logger: true })

fastify.register(require('./routes/user'), { logLevel: 'warn' })
fastify.register(require('./routes/events'), { logLevel: 'debug' })

fastify.listen({ port: 3000 })

または、ルートに直接渡すこともできます。

fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})

カスタムログレベルはルートのみに適用され、fastify.logでアクセスできるグローバルなFastify Loggerには適用されないことに注意してください。

カスタムログシリアライザ

状況によっては、大きなオブジェクトをログに記録する必要がある場合がありますが、一部のルートではリソースの無駄になる可能性があります。この場合、カスタムserializersを定義し、適切なコンテキストにアタッチできます!

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

fastify.register(require('./routes/user'), {
logSerializers: {
user: (value) => `My serializer one - ${value.name}`
}
})
fastify.register(require('./routes/events'), {
logSerializers: {
user: (value) => `My serializer two - ${value.name} ${value.surname}`
}
})

fastify.listen({ port: 3000 })

コンテキストによってシリアライザを継承できます。

const fastify = Fastify({
logger: {
level: 'info',
serializers: {
user (req) {
return {
method: req.method,
url: req.url,
headers: req.headers,
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket.remotePort
}
}
}
}
})

fastify.register(context1, {
logSerializers: {
user: value => `My serializer father - ${value}`
}
})

async function context1 (fastify, opts) {
fastify.get('/', (req, reply) => {
req.log.info({ user: 'call father serializer', key: 'another key' })
// shows: { user: 'My serializer father - call father serializer', key: 'another key' }
reply.send({})
})
}

fastify.listen({ port: 3000 })

設定

新しいハンドラーを登録する際に、設定オブジェクトを渡してハンドラー内で取得できます。

// server.js
const fastify = require('fastify')()

function handler (req, reply) {
reply.send(reply.routeOptions.config.output)
}

fastify.get('/en', { config: { output: 'hello world!' } }, handler)
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)

fastify.listen({ port: 3000 })

制約

Fastifyは、Hostヘッダーなどのリクエストのプロパティに基づいて、特定のリクエストのみに一致するようにルートを制約することをサポートしています。find-my-way制約を介して他の値も使用できます。制約は、ルートオプションのconstraintsプロパティで指定します。Fastifyには、version制約とhost制約という2つの組み込み制約が用意されており、リクエストの他の部分を検査して、リクエストに対してルートを実行するかどうかを決定するために、独自の カスタム制約戦略を追加できます。

バージョン制約

ルートにconstraintsオプションでversionキーを指定できます。バージョン付きルートを使用すると、同じHTTPルートパスに対して複数のハンドラーを宣言できます。その後、各リクエストのAccept-Versionヘッダーに従って一致させます。Accept-Versionヘッダー値はsemver仕様に従う必要があり、ルートは一致させるために正確なsemverバージョンで宣言する必要があります。

ルートにバージョンが設定されている場合、FastifyはリクエストのAccept-Versionヘッダーが設定されていることを要求し、同じパスのバージョンなしのルートよりもバージョン付きのルートを優先します。現在、高度なバージョン範囲とプレリリースはサポートされていません。

この機能を使用すると、ルーターの全体的なパフォーマンスが低下することに注意してください。

fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' },
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})

fastify.inject({
method: 'GET',
url: '/',
headers: {
'Accept-Version': '1.x' // it could also be '1.2.0' or '1.2.x'
}
}, (err, res) => {
// { hello: 'world' }
})

⚠ セキュリティに関する注意事項

バージョン付けの定義に使用している値(例:'Accept-Version')を使用して、レスポンスにVaryヘッダーを設定して、キャッシュポイズニング攻撃を防いでください。プロキシ/CDNの一部として設定することもできます。

const append = require('vary').append
fastify.addHook('onSend', (req, reply, payload, done) => {
if (req.headers['accept-version']) { // or the custom header you are using
let value = reply.getHeader('Vary') || ''
const header = Array.isArray(value) ? value.join(', ') : String(value)
if ((value = append(header, 'Accept-Version'))) { // or the custom header you are using
reply.header('Vary', value)
}
}
done()
})

同じメジャーまたはマイナーで複数のバージョンを宣言する場合、Fastifyは常にAccept-Versionヘッダー値と互換性のある最も高いバージョンを選択します。

リクエストにAccept-Versionヘッダーがない場合、404エラーが返されます。

カスタムバージョンマッチングロジックを定義できます。これは、Fastifyサーバーインスタンスを作成する際のconstraints設定を使用して実行できます。

ホスト制約

リクエストのHostヘッダーの特定の値のみに一致するようにルートを制限するために、constraintsルートオプションにhostキーを指定できます。host制約値は、完全一致の場合は文字列として、任意のホスト一致の場合は正規表現として指定できます。

fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'auth.fastify.dev' },
handler: function (request, reply) {
reply.send('hello world from auth.fastify.dev')
}
})

fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'example.com'
}
}, (err, res) => {
// 404 because the host doesn't match the constraint
})

fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'auth.fastify.dev'
}
}, (err, res) => {
// => 'hello world from auth.fastify.dev'
})

ワイルドカードサブドメイン(またはその他の任意のパターン)に一致するホストを制約することも、正規表現host制約を指定することで可能です。

fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.dev/ }, // will match any subdomain of fastify.dev
handler: function (request, reply) {
reply.send('hello world from ' + request.headers.host)
}
})

非同期カスタム制約

カスタム制約を提供でき、databaseなどの別のソースからconstraint基準を取得できます。非同期カスタム制約の使用は、ルーターのパフォーマンスに影響を与えるため、最後の手段としてください。

function databaseOperation(field, done) {
done(null, field)
}

const secret = {
// strategy name for referencing in the route handler `constraints` options
name: 'secret',
// storage factory for storing routes in the find-my-way route tree
storage: function () {
let handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
// function to get the value of the constraint from each incoming request
deriveConstraint: (req, ctx, done) => {
databaseOperation(req.headers['secret'], done)
},
// optional flag marking if handlers without constraints can match requests that have a value for this constraint
mustMatchWhenDerived: true
}

⚠ セキュリティに関する注意事項

非同期制約と共に使用する場合、コールバック内でエラーを返すことは決して推奨されません。エラーを回避できない場合は、カスタムframeworkErrorsハンドラーを用意して処理することをお勧めします。そうでない場合、ルートの選択が中断されるか、攻撃者に機密情報が公開される可能性があります。

const Fastify = require('fastify')

const fastify = Fastify({
frameworkErrors: function (err, res, res) {
if (err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
res.code(400)
return res.send("Invalid header provided")
} else {
res.send(err)
}
}
})