UNPKG

6.54 kBMarkdownView Raw
1<h1 align="center">Fastify</h1>
2
3## Decorators
4
5The decorators API allows customization of the core Fastify objects, such as
6the server instance itself and any request and reply objects used during the
7HTTP request lifecycle. The decorators API can be used to attach any type of
8property to the core objects, e.g. functions, plain objects, or native types.
9
10This API is a *synchronous* API. Attempting to define a decoration
11asynchronously could result in the Fastify instance booting prior to the
12decoration completing its initialization. To avoid this issue, and register an
13asynchronous decoration, the `register` API, in combination with
14`fastify-plugin`, must be used instead. To learn more, see the
15[Plugins](Plugins.md) documentation.
16
17Decorating core objects with this API allows the underlying JavaScript engine
18to optimize handling of the server, request, and reply objects. This is
19accomplished by defining the shape of all such object instances before they are
20instantiated and used. As an example, the following is not recommended because
21it will change the shape of objects during their lifecycle:
22
23```js
24// Bad example! Continue reading.
25
26// Attach a user property to the incoming request before the request
27// handler is invoked.
28fastify.addHook('preHandler', function (req, reply, done) {
29 req.user = 'Bob Dylan'
30 done()
31})
32
33// Use the attached user property in the request handler.
34fastify.get('/', function (req, reply) {
35 reply.send(`Hello, ${req.user}`)
36})
37```
38
39Since the above example mutates the request object after it has already
40been instantiated, the JavaScript engine must deoptimize access to the request
41object. By using the decoration API this deoptimization is avoided:
42
43```js
44// Decorate request with a 'user' property
45fastify.decorateRequest('user', '')
46
47// Update our property
48fastify.addHook('preHandler', (req, reply, done) => {
49 req.user = 'Bob Dylan'
50 done()
51})
52// And finally access it
53fastify.get('/', (req, reply) => {
54 reply.send(`Hello, ${req.user}!`)
55})
56```
57
58See
59[JavaScript engine fundamentals: Shapes and Inline Caches](https://web.archive.org/web/20200201163000/https://mathiasbynens.be/notes/shapes-ics)
60for more information on this topic.
61
62### Usage
63<a name="usage"></a>
64
65#### `decorate(name, value, [dependencies])`
66<a name="decorate"></a>
67
68This method is used to customize the Fastify [server](Server.md) instance.
69
70For example, to attach a new method to the server instance:
71
72```js
73fastify.decorate('utility', function () {
74 // Something very useful
75})
76```
77
78As mentioned above, non-function values can be attached:
79
80```js
81fastify.decorate('conf', {
82 db: 'some.db',
83 port: 3000
84})
85```
86
87To access decorated properties, simply use the name provided to the
88decoration API:
89
90```js
91fastify.utility()
92
93console.log(fastify.conf.db)
94```
95
96The `dependencies` parameter is an optional list of decorators that the
97decorator being defined relies upon. This list is simply a list of string names
98of other decorators. In the following example, the "utility" decorator depends
99upon "greet" and "log" decorators:
100
101```js
102fastify.decorate('utility', fn, ['greet', 'log'])
103```
104
105If a dependency is not satisfied, the `decorate` method will throw an exception.
106The dependency check is peformed before the server instance is booted. Thus,
107it cannot occur during runtime.
108
109#### `decorateReply(name, value, [dependencies])`
110<a name="decorate-reply"></a>
111
112As the name suggests, this API is used to add new methods/properties to the core
113`Reply` object:
114
115```js
116fastify.decorateReply('utility', function () {
117 // Something very useful
118})
119```
120
121Note: using an arrow function will break the binding of `this` to the Fastify
122`Reply` instance.
123
124See [`decorate`](#decorate) for information about the `dependencies` parameter.
125
126#### `decorateRequest(name, value, [dependencies])`
127<a name="decorate-request"></a>
128
129As above with [`decorateReply`](#decorate-reply), this API is used add new
130methods/properties to the core `Request` object:
131
132```js
133fastify.decorateRequest('utility', function () {
134 // something very useful
135})
136```
137
138Note: using an arrow function will break the binding of `this` to the Fastify
139`Request` instance.
140
141See [`decorate`](#decorate) for information about the `dependencies` parameter.
142
143#### `hasDecorator(name)`
144<a name="has-decorator"></a>
145
146Used to check for the existence of a server instance decoration:
147
148```js
149fastify.hasDecorator('utility')
150```
151
152#### hasRequestDecorator
153<a name="has-request-decorator"></a>
154
155Used to check for the existence of a Request decoration:
156
157```js
158fastify.hasRequestDecorator('utility')
159```
160
161#### hasReplyDecorator
162<a name="has-reply-decorator"></a>
163
164Used to check for the existence of a Reply decoration:
165
166```js
167fastify.hasReplyDecorator('utility')
168```
169
170### Decorators and Encapsulation
171<a name="decorators-encapsulation"></a>
172
173Defining a decorator (using `decorate`, `decorateRequest` or `decorateReply`)
174with the same name more than once in the same **encapsulated** context will
175throw an exception.
176
177As an example, the following will throw:
178
179```js
180const server = require('fastify')()
181
182server.decorateReply('view', function (template, args) {
183 // Amazing view rendering engine
184})
185
186server.get('/', (req, reply) => {
187 reply.view('/index.html', { hello: 'world' })
188})
189
190// Somewhere else in our codebase, we define another
191// view decorator. This throws.
192server.decorateReply('view', function (template, args) {
193 // Another rendering engine
194})
195
196server.listen(3000)
197```
198
199
200But this will not:
201
202```js
203const server = require('fastify')()
204
205server.decorateReply('view', function (template, args) {
206 // Amazing view rendering engine.
207})
208
209server.register(async function (server, opts) {
210 // We add a view decorator to the current encapsulated
211 // plugin. This will not throw as outside of this encapsulated
212 // plugin view is the old one, while inside it is the new one.
213 server.decorateReply('view', function (template, args) {
214 // Another rendering engine
215 })
216
217 server.get('/', (req, reply) => {
218 reply.view('/index.page', { hello: 'world' })
219 })
220}, { prefix: '/bar' })
221
222server.listen(3000)
223```
224
225### Getters and Setters
226<a name="getters-setters"></a>
227
228Decorators accept special "getter/setter" objects. These objects have functions
229named `getter` and `setter` (though, the `setter` function is optional). This
230allows defining properties via decorators. For example:
231
232```js
233fastify.decorate('foo', {
234 getter () {
235 return 'a getter'
236 }
237})
238```
239
240Will define the `foo` property on the Fastify instance:
241
242```js
243console.log(fastify.foo) // 'a getter'
244```