メインコンテンツにスキップ
バージョン: 最新 (v5.0.x)

デコレーター

デコレーター

デコレーターAPIは、サーバーインスタンス自体やHTTPリクエストライフサイクル中に使用されるリクエストおよび返信オブジェクトなど、Fastifyのコアオブジェクトのカスタマイズを可能にします。デコレーターAPIを使用して、関数、プレーンオブジェクト、ネイティブ型など、あらゆる種類のプロパティをコアオブジェクトにアタッチできます。

このAPIは*同期*です。デコレーションを非同期的に定義しようとすると、デコレーションが初期化を完了する前にFastifyインスタンスが起動する可能性があります。この問題を回避し、非同期デコレーションを登録するには、代わりに`register` APIと`fastify-plugin`を組み合わせて使用する必要があります。詳しくは、プラグインのドキュメントをご覧ください。

このAPIを使用してコアオブジェクトをデコレーションすることで、基盤となるJavaScriptエンジンはサーバー、リクエスト、および返信オブジェクトの処理を最適化できます。これは、すべてのオブジェクトインスタンスがインスタンス化および使用される前に、それらの形状を定義することによって実現されます。例として、以下は推奨されません。オブジェクトのライフサイクル中に形状が変更されるためです。

// Bad example! Continue reading.

// Attach a user property to the incoming request before the request
// handler is invoked.
fastify.addHook('preHandler', function (req, reply, done) {
req.user = 'Bob Dylan'
done()
})

// Use the attached user property in the request handler.
fastify.get('/', function (req, reply) {
reply.send(`Hello, ${req.user}`)
})

上記の例では、リクエストオブジェクトがインスタンス化された後に変更されるため、JavaScriptエンジンはリクエストオブジェクトへのアクセスを最適化解除する必要があります。デコレーションAPIを使用することで、この最適化解除を回避できます。

// Decorate request with a 'user' property
fastify.decorateRequest('user', '')

// Update our property
fastify.addHook('preHandler', (req, reply, done) => {
req.user = 'Bob Dylan'
done()
})
// And finally access it
fastify.get('/', (req, reply) => {
reply.send(`Hello, ${req.user}!`)
})

デコレーションされたフィールドの初期形状は、将来動的に設定される予定の値にできるだけ近づけることが重要です。意図した値が文字列の場合はデコレーターを`''`として初期化し、オブジェクトまたは関数の場合は`null`として初期化します。

この例は値型でのみ機能することに注意してください。参照型はfastifyの起動中にエラーをスローします。decorateRequestを参照してください。

このトピックの詳細については、JavaScriptエンジン fundamentals: Shapes and Inline Cachesを参照してください。

使用方法

`decorate(name, value, [dependencies])`

このメソッドは、Fastify サーバーインスタンスをカスタマイズするために使用されます。

たとえば、サーバーインスタンスに新しいメソッドをアタッチするには、次のようにします。

fastify.decorate('utility', function () {
// Something very useful
})

上記のように、関数以外の値は次のようにサーバーインスタンスにアタッチできます。

fastify.decorate('conf', {
db: 'some.db',
port: 3000
})

デコレーションされたプロパティにアクセスするには、デコレーションAPIに提供された名前を使用します。

fastify.utility()

console.log(fastify.conf.db)

デコレーションされたFastifyサーバーは、ルートハンドラーの`this`にバインドされています。

fastify.decorate('db', new DbConnection())

fastify.get('/', async function (request, reply) {
// using return
return { hello: await this.db.query('world') }

// or
// using reply.send()
reply.send({ hello: await this.db.query('world') })
await reply
})

`dependencies`パラメータは、定義されているデコレーターが依存するデコレーターのオプションのリストです。このリストは、他のデコレーターの文字列名のリストです。次の例では、「utility」デコレーターは「greet」および「hi」デコレーターに依存しています。

async function greetDecorator (fastify, opts) {
fastify.decorate('greet', () => {
return 'greet message'
})
}

async function hiDecorator (fastify, opts) {
fastify.decorate('hi', () => {
return 'hi message'
})
}

async function utilityDecorator (fastify, opts) {
fastify.decorate('utility', () => {
return `${fastify.greet()} | ${fastify.hi()}`
})
}

fastify.register(fastifyPlugin(greetDecorator, { name: 'greet' }))
fastify.register(fastifyPlugin(hiDecorator, { name: 'hi' }))
fastify.register(fastifyPlugin(utilityDecorator, { dependencies: ['greet', 'hi'] }))

fastify.get('/', function (req, reply) {
// Response: {"hello":"greet message | hi message"}
reply.send({ hello: fastify.utility() })
})

fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
})

注:アロー関数を使用すると、`this`の`FastifyInstance`へのバインディングが壊れます。

依存関係が満たされない場合、`decorate`メソッドは例外をスローします。依存関係チェックは、サーバーインスタンスが起動される前に行われます。したがって、実行時には発生しません。

`decorateReply(name, value, [dependencies])`

名前が示すように、このAPIはコア`Reply`オブジェクトに新しいメソッド/プロパティを追加するために使用されます。

fastify.decorateReply('utility', function () {
// Something very useful
})

注:アロー関数を使用すると、`this`のFastify `Reply`インスタンスへのバインディングが壊れます。

注:`decorateReply`は、参照型で使用するとエラーをスローします。

// Don't do this
fastify.decorateReply('foo', { bar: 'fizz'})

この例では、オブジェクトの参照がすべてのリクエストで共有され、**すべての変更がすべてのリクエストに影響を与え、セキュリティの脆弱性やメモリリークが発生する可能性があります**。そのため、Fastifyはこれをブロックします。

リクエスト全体で適切なカプセル化を実現するには、`'onRequest'`フックで受信リクエストごとに新しい値を設定します。例:

const fp = require('fastify-plugin')

async function myPlugin (app) {
app.decorateRequest('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
})
}

module.exports = fp(myPlugin)

`dependencies`パラメータについては、`decorate`を参照してください。

`decorateRequest(name, value, [dependencies])`

上記の`decorateReply`と同様に、このAPIはコア`Request`オブジェクトに新しいメソッド/プロパティを追加するために使用されます。

fastify.decorateRequest('utility', function () {
// something very useful
})

注:アロー関数を使用すると、`this`のFastify `Request`インスタンスへのバインディングが壊れます。

注:`decorateRequest`は、参照型で使用するとエラーを発行します。

// Don't do this
fastify.decorateRequest('foo', { bar: 'fizz'})

この例では、オブジェクトの参照がすべてのリクエストで共有され、**すべての変更がすべてのリクエストに影響を与え、セキュリティの脆弱性やメモリリークが発生する可能性があります**。そのため、Fastifyはこれをブロックします。

リクエスト全体で適切なカプセル化を実現するには、 `'onRequest'`フックで着信リクエストごとに新しい値を設定します。

例:

const fp = require('fastify-plugin')

async function myPlugin (app) {
app.decorateRequest('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
})
}

module.exports = fp(myPlugin)

フックソリューションはより柔軟で、`onRequest`フックにロジックを追加できるため、より複雑な初期化が可能です。

別の方法は、ゲッター/セッターパターンを使用することですが、2つのデコレーターが必要です。

fastify.decorateRequest('my_decorator_holder') // define the holder
fastify.decorateRequest('user', {
getter () {
this.my_decorator_holder ??= {} // initialize the holder
return this.my_decorator_holder
}
})

fastify.get('/', async function (req, reply) {
req.user.access = 'granted'
// other code
})

これにより、`user`プロパティがリクエストごとに常に一意であることが保証されます。

`dependencies`パラメータについては、`decorate`を参照してください。

`hasDecorator(name)`

サーバーインスタンスデコレーションの存在を確認するために使用されます。

fastify.hasDecorator('utility')

hasRequestDecorator

リクエストデコレーションの存在を確認するために使用されます。

fastify.hasRequestDecorator('utility')

hasReplyDecorator

返信デコレーションの存在を確認するために使用されます。

fastify.hasReplyDecorator('utility')

デコレーターとカプセル化

同じ**カプセル化された**コンテキストで同じ名前のデコレーター(`decorate`、`decorateRequest`、または`decorateReply`を使用)を複数回定義すると、例外がスローされます。

例として、以下はスローされます。

const server = require('fastify')()

server.decorateReply('view', function (template, args) {
// Amazing view rendering engine
})

server.get('/', (req, reply) => {
reply.view('/index.html', { hello: 'world' })
})

// Somewhere else in our codebase, we define another
// view decorator. This throws.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})

server.listen({ port: 3000 })

しかし、これはスローされません。

const server = require('fastify')()

server.decorateReply('view', function (template, args) {
// Amazing view rendering engine.
})

server.register(async function (server, opts) {
// We add a view decorator to the current encapsulated
// plugin. This will not throw as outside of this encapsulated
// plugin view is the old one, while inside it is the new one.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})

server.get('/', (req, reply) => {
reply.view('/index.page', { hello: 'world' })
})
}, { prefix: '/bar' })

server.listen({ port: 3000 })

ゲッターとセッター

デコレーターは特別な「ゲッター/セッター」オブジェクトを受け入れます。これらのオブジェクトには、`getter`および`setter`という名前の関数があります(ただし、`setter`関数はオプションです)。これにより、デコレーターを介してプロパティを定義できます。例:

fastify.decorate('foo', {
getter () {
return 'a getter'
}
})

Fastifyインスタンスに`foo`プロパティを定義します。

console.log(fastify.foo) // 'a getter'