カプセル化
カプセル化
Fastify の基本的な機能は「カプセル化コンテキスト」です。カプセル化コンテキストは、どのデコレータ、登録されたフック、およびプラグインがルートで利用可能かを制御します。カプセル化コンテキストを視覚的に表現したものが次の図です。
上の図には、いくつかのエンティティがあります。
- ルートコンテキスト
- 3 つのルートプラグイン
- 2 つの子コンテキスト。それぞれの子コンテキストには、
- 2 つの子プラグイン
- 1 つの孫コンテキスト。それぞれの孫コンテキストには、
- 3 つの子プラグイン
すべての子コンテキストと孫コンテキストは、ルートプラグインにアクセスできます。各子コンテキスト内では、孫コンテキストは、含まれている子コンテキスト内に登録された子プラグインにアクセスできますが、含まれている子コンテキストは、その孫コンテキスト内に登録された子プラグインにアクセスすることはできません。
Fastify のすべては、ルートコンテキストを除いてプラグインであるため、この例のすべての「コンテキスト」と「プラグイン」は、デコレータ、フック、プラグイン、およびルートで構成できるプラグインです。したがって、この例を具体的に説明するために、3 つのルートを持つ REST API サーバーの基本的なシナリオを考えてみましょう。最初のルート (/one
) は認証が必要で、2 番目のルート (/two
) は認証が不要で、3 番目のルート (/three
) は 2 番目のルートと同じコンテキストにアクセスできます。@fastify/bearer-auth を使用して認証を提供する場合、この例のコードは次のようになります。
'use strict'
const fastify = require('fastify')()
fastify.decorateRequest('answer', 42)
fastify.register(async function authenticatedContext (childServer) {
childServer.register(require('@fastify/bearer-auth'), { keys: ['abc123'] })
childServer.route({
path: '/one',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
// request.foo will be undefined as it's only defined in publicContext
foo: request.foo,
// request.bar will be undefined as it's only defined in grandchildContext
bar: request.bar
})
}
})
})
fastify.register(async function publicContext (childServer) {
childServer.decorateRequest('foo', 'foo')
childServer.route({
path: '/two',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
// request.bar will be undefined as it's only defined in grandchildContext
bar: request.bar
})
}
})
childServer.register(async function grandchildContext (grandchildServer) {
grandchildServer.decorateRequest('bar', 'bar')
grandchildServer.route({
path: '/three',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
})
})
fastify.listen({ port: 8000 })
上記のサーバーの例は、元の図に示されているすべてのカプセル化の概念を示しています。
- 各子コンテキスト (
authenticatedContext
、publicContext
、およびgrandchildContext
) は、ルートコンテキストで定義されたanswer
リクエストデコレータにアクセスできます。 authenticatedContext
のみが@fastify/bearer-auth
プラグインにアクセスできます。publicContext
とgrandchildContext
の両方がfoo
リクエストデコレータにアクセスできます。grandchildContext
のみがbar
リクエストデコレータにアクセスできます。
これを確認するには、サーバーを起動してリクエストを発行します。
# curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one
{"answer":42}
# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}
コンテキスト間の共有
前の例の各コンテキストは、親コンテキストからのみ継承することに注意してください。親コンテキストは、子孫コンテキスト内のエンティティにアクセスできません。このデフォルトは、場合によっては望ましくありません。そのような場合、fastify-plugin を使用することでカプセル化コンテキストを破り、子孫コンテキストに登録されているものがすべて、含まれている親コンテキストで利用できるようにすることができます。
前の例で、publicContext
がgrandchildContext
内で定義されたbar
デコレータにアクセスする必要があると仮定すると、コードは次のように書き直すことができます。
'use strict'
const fastify = require('fastify')()
const fastifyPlugin = require('fastify-plugin')
fastify.decorateRequest('answer', 42)
// `authenticatedContext` omitted for clarity
fastify.register(async function publicContext (childServer) {
childServer.decorateRequest('foo', 'foo')
childServer.route({
path: '/two',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
childServer.register(fastifyPlugin(grandchildContext))
async function grandchildContext (grandchildServer) {
grandchildServer.decorateRequest('bar', 'bar')
grandchildServer.route({
path: '/three',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
}
})
fastify.listen({ port: 8000 })
サーバーを再起動し、/two
と/three
のリクエストを再発行します。
# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo","bar":"bar"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}