fastify 6.0.0-alpha.5 → 6.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +1 -0
  2. package/build/build-validation.js +2 -0
  3. package/docs/Guides/Ecosystem.md +4 -0
  4. package/docs/Guides/Migration-Guide-V5.md +2 -2
  5. package/docs/Guides/Serverless.md +33 -10
  6. package/docs/Reference/Errors.md +13 -0
  7. package/docs/Reference/Hooks.md +13 -0
  8. package/docs/Reference/Reply.md +786 -697
  9. package/docs/Reference/Request.md +303 -107
  10. package/docs/Reference/Server.md +39 -2
  11. package/docs/Reference/Validation-and-Serialization.md +15 -1
  12. package/fastify.d.ts +1 -0
  13. package/fastify.js +35 -1
  14. package/lib/config-validator.js +102 -73
  15. package/lib/content-type-parser.js +14 -2
  16. package/lib/context.js +2 -1
  17. package/lib/decorate.js +4 -3
  18. package/lib/error-handler.js +21 -4
  19. package/lib/error-status.js +4 -2
  20. package/lib/errors.js +15 -9
  21. package/lib/reply.js +12 -11
  22. package/lib/request.js +3 -2
  23. package/lib/route.js +34 -5
  24. package/lib/schemas.js +44 -32
  25. package/package.json +8 -3
  26. package/test/close-disposable.test.js +154 -0
  27. package/test/constrained-routes.test.js +4 -2
  28. package/test/content-parser.test.js +39 -0
  29. package/test/decorator.test.js +29 -0
  30. package/test/diagnostics-channel/error-status.test.js +35 -2
  31. package/test/handler-timeout.test.js +224 -0
  32. package/test/internals/all.test.js +5 -0
  33. package/test/internals/errors.test.js +18 -9
  34. package/test/internals/initial-config.test.js +2 -0
  35. package/test/internals/server.test.js +6 -4
  36. package/test/reply-error.test.js +82 -0
  37. package/test/reply-trailers.test.js +159 -1
  38. package/test/router-compile.test.js +241 -0
  39. package/test/schema-serialization.test.js +120 -7
  40. package/test/types/fastify.tst.ts +4 -0
  41. package/test/types/instance.tst.ts +1 -1
  42. package/test/types/route.tst.ts +16 -0
  43. package/test/types/using.tst.ts +20 -0
  44. package/test/validation-error-handling.test.js +21 -1
  45. package/types/instance.d.ts +5 -1
  46. package/types/route.d.ts +1 -0
@@ -1,45 +1,52 @@
1
1
  <h1 align="center">Fastify</h1>
2
2
 
3
3
  ## Reply
4
- - [Reply](#reply)
5
- - [Introduction](#introduction)
6
- - [.code(statusCode)](#codestatuscode)
7
- - [.elapsedTime](#elapsedtime)
8
- - [.statusCode](#statuscode)
9
- - [.mediaType](#mediatype)
10
- - [.server](#server)
11
- - [.header(key, value)](#headerkey-value)
12
- - [.headers(object)](#headersobject)
13
- - [.getHeader(key)](#getheaderkey)
14
- - [.getHeaders()](#getheaders)
15
- - [.removeHeader(key)](#removeheaderkey)
16
- - [.hasHeader(key)](#hasheaderkey)
17
- - [.writeEarlyHints(hints, callback)](#writeearlyhintshints-callback)
18
- - [.trailer(key, function)](#trailerkey-function)
19
- - [.hasTrailer(key)](#hastrailerkey)
20
- - [.removeTrailer(key)](#removetrailerkey)
21
- - [.redirect(dest, [code ,])](#redirectdest--code)
22
- - [.callNotFound()](#callnotfound)
23
- - [.type(contentType)](#typecontenttype)
24
- - [.getSerializationFunction(schema | httpStatus, [contentType])](#getserializationfunctionschema--httpstatus)
25
- - [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschemaschema-httpstatus)
26
- - [.serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])](#serializeinputdata-schema--httpstatus-httpstatus)
27
- - [.serializer(func)](#serializerfunc)
28
- - [.raw](#raw)
29
- - [.sent](#sent)
30
- - [.hijack()](#hijack)
31
- - [.send(data)](#senddata)
32
- - [Objects](#objects)
33
- - [Strings](#strings)
34
- - [Streams](#streams)
35
- - [Buffers](#buffers)
36
- - [TypedArrays](#typedarrays)
37
- - [ReadableStream](#readablestream)
38
- - [Response](#response)
39
- - [Errors](#errors)
40
- - [Type of the final payload](#type-of-the-final-payload)
41
- - [Async-Await and Promises](#async-await-and-promises)
42
- - [.then(fulfilled, rejected)](#thenfulfilled-rejected)
4
+ - [Introduction](#introduction)
5
+ - [Reply](#reply-object)
6
+ - [Properties](#properties)
7
+ - [.statusCode](#statuscode)
8
+ - [.sent](#sent)
9
+ - [.request](#request)
10
+ - [.log](#log)
11
+ - [.raw](#raw)
12
+ - [.server](#server)
13
+ - [.routeOptions](#routeoptions)
14
+ - [.mediaType](#mediatype)
15
+ - [.elapsedTime](#elapsedtime)
16
+ - [Methods](#methods)
17
+ - [.send(data)](#senddata)
18
+ - [Objects](#objects)
19
+ - [Strings](#strings)
20
+ - [Buffers](#buffers)
21
+ - [TypedArrays](#typedarrays)
22
+ - [Streams](#streams)
23
+ - [ReadableStream](#readablestream)
24
+ - [Response](#response)
25
+ - [Errors](#errors)
26
+ - [Async-Await and Promises](#async-await-and-promises)
27
+ - [Type of the final payload](#type-of-the-final-payload)
28
+ - [.code(statusCode)](#codestatuscode)
29
+ - [.header(key, value)](#headerkey-value)
30
+ - [.headers(object)](#headersobject)
31
+ - [.type(contentType)](#typecontenttype)
32
+ - [.redirect(dest, [code])](#redirectdest-code)
33
+ - [.getHeader(key)](#getheaderkey)
34
+ - [.getHeaders()](#getheaders)
35
+ - [.hasHeader(key)](#hasheaderkey)
36
+ - [.removeHeader(key)](#removeheaderkey)
37
+ - [.callNotFound()](#callnotfound)
38
+ - [.hijack()](#hijack)
39
+ - [.serializer(func)](#serializerfunc)
40
+ - [.serialize(payload)](#serializepayload)
41
+ - [.trailer(key, function)](#trailerkey-function)
42
+ - [.hasTrailer(key)](#hastrailerkey)
43
+ - [.removeTrailer(key)](#removetrailerkey)
44
+ - [.writeEarlyHints(hints, callback)](#writeearlyhintshints-callback)
45
+ - [.then(fulfilled, rejected)](#thenfulfilled-rejected)
46
+ - [.getDecorator(name)](#getdecoratorname)
47
+ - [.getSerializationFunction(schema | httpStatus, [contentType])](#getserializationfunctionschema--httpstatus-contenttype)
48
+ - [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschemaschema-httpstatus-contenttype)
49
+ - [.serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])](#serializeinputdata-schema--httpstatus-httpstatus-contenttype)
43
50
 
44
51
  ### Introduction
45
52
  <a id="introduction"></a>
@@ -66,7 +73,7 @@ since the request was received by Fastify.
66
73
  - `.hasTrailer(key)` - Determine if a trailer has been set.
67
74
  - `.removeTrailer(key)` - Remove the value of a previously set trailer.
68
75
  - `.type(value)` - Sets the header `Content-Type`.
69
- - `.redirect(dest, [code,])` - Redirect to the specified URL, the status code is
76
+ - `.redirect(dest, [code])` - Redirect to the specified URL, the status code is
70
77
  optional (defaults to `302`).
71
78
  - `.callNotFound()` - Invokes the custom not found handler.
72
79
  - `.serialize(payload)` - Serializes the specified payload using the default
@@ -95,6 +102,7 @@ since the request was received by Fastify.
95
102
  from Node core.
96
103
  - `.log` - The logger instance of the incoming request.
97
104
  - `.request` - The incoming request.
105
+ - `.getDecorator(name)` - Returns the value of a reply decorator.
98
106
 
99
107
  ```js
100
108
  fastify.get('/', options, function (request, reply) {
@@ -106,22 +114,13 @@ fastify.get('/', options, function (request, reply) {
106
114
  })
107
115
  ```
108
116
 
109
- ### .code(statusCode)
110
- <a id="code"></a>
111
-
112
- If not set via `reply.code`, the resulting `statusCode` will be `200`.
113
-
114
- ### .elapsedTime
115
- <a id="elapsedTime"></a>
116
-
117
- Invokes the custom response time getter to calculate the amount of time passed
118
- since the request was received by Fastify.
117
+ ### Reply
118
+ <a id="reply-object"></a>
119
119
 
120
- ```js
121
- const milliseconds = reply.elapsedTime
122
- ```
120
+ #### Properties
121
+ <a id="properties"></a>
123
122
 
124
- ### .statusCode
123
+ ##### .statusCode
125
124
  <a id="statusCode"></a>
126
125
 
127
126
  This property reads and sets the HTTP status code. It is an alias for
@@ -131,20 +130,69 @@ if (reply.statusCode >= 299) {
131
130
  reply.statusCode = 500
132
131
  }
133
132
  ```
134
- ### .mediaType
135
- <a id="mediatype"></a>
136
133
 
137
- Returns the media type extracted from `Content-Type` header. When `Content-Type`
138
- header is missing, it will return `undefined`.
134
+ ##### .sent
135
+ <a id="sent"></a>
136
+
137
+ As the name suggests, `.sent` is a property to indicate if a response has been
138
+ sent via `reply.send()`. It will also be `true` in case `reply.hijack()` was
139
+ used.
140
+
141
+ In case a route handler is defined as an async function or it returns a promise,
142
+ it is possible to call `reply.hijack()` to indicate that the automatic
143
+ invocation of `reply.send()` once the handler promise resolve should be skipped.
144
+ By calling `reply.hijack()`, an application claims full responsibility for the
145
+ low-level request and response. Moreover, hooks will not be invoked.
146
+
147
+ ##### .request
148
+ <a id="request"></a>
149
+
150
+ The [`Request`](./Request.md) object of the incoming request this reply belongs
151
+ to.
139
152
 
140
153
  ```js
141
- if (reply.mediaType === 'image/gif') {
142
- const versionBuffer = new Uint8Array(reply.payload, 3, 3)
143
- reply.header('x-gif-version', new TextDecoder().decode(versionBuffer))
144
- }
154
+ fastify.addHook('onSend', async function (request, reply, payload) {
155
+ reply.header('x-request-url', reply.request.url)
156
+ })
145
157
  ```
146
158
 
147
- ### .server
159
+ ##### .log
160
+ <a id="log"></a>
161
+
162
+ The logger instance of the incoming request. It is the same instance as
163
+ `request.log`, so every log line includes the request id. See
164
+ [Logging](./Logging.md) for more information.
165
+
166
+ ```js
167
+ fastify.get('/', function (request, reply) {
168
+ reply.log.info('sending the response')
169
+ reply.send({ hello: 'world' })
170
+ })
171
+ ```
172
+
173
+ ##### .raw
174
+ <a id="raw"></a>
175
+
176
+ This is the
177
+ [`http.ServerResponse`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_class_http_serverresponse)
178
+ from Node core. Whilst you are using the Fastify `Reply` object, the use of
179
+ `Reply.raw` functions is at your own risk as you are skipping all the Fastify
180
+ logic of handling the HTTP response. e.g.:
181
+
182
+ ```js
183
+ app.get('/cookie-2', (req, reply) => {
184
+ reply.setCookie('session', 'value', { secure: false }) // this will not be used
185
+
186
+ // in this case we are using only the nodejs http server response object
187
+ reply.raw.writeHead(200, { 'Content-Type': 'text/plain' })
188
+ reply.raw.write('ok')
189
+ reply.raw.end()
190
+ })
191
+ ```
192
+ Another example of the misuse of `Reply.raw` is explained in
193
+ [Reply](#getheaders).
194
+
195
+ ##### .server
148
196
  <a id="server"></a>
149
197
 
150
198
  The Fastify server instance, scoped to the current [encapsulation
@@ -160,440 +208,571 @@ fastify.get('/', async function (req, rep) {
160
208
  })
161
209
  ```
162
210
 
163
- ### .header(key, value)
164
- <a id="header"></a>
165
-
166
- Sets a response header. If the value is omitted or undefined, it is coerced to
167
- `''`.
168
-
169
- > ℹ️ Note:
170
- > The header's value must be properly encoded using
171
- > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
172
- > or similar modules such as
173
- > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid characters
174
- > will result in a 500 `TypeError` response.
175
-
176
- For more information, see
177
- [`http.ServerResponse#setHeader`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_response_setheader_name_value).
178
-
179
- - ### set-cookie
180
- <a id="set-cookie"></a>
181
-
182
- - When sending different values as a cookie with `set-cookie` as the key,
183
- every value will be sent as a cookie instead of replacing the previous
184
- value.
185
-
186
- ```js
187
- reply.header('set-cookie', 'foo');
188
- reply.header('set-cookie', 'bar');
189
- ```
190
- - The browser will only consider the latest reference of a key for the
191
- `set-cookie` header. This is done to avoid parsing the `set-cookie` header
192
- when added to a reply and speeds up the serialization of the reply.
193
-
194
- - To reset the `set-cookie` header, you need to make an explicit call to
195
- `reply.removeHeader('set-cookie')`, read more about `.removeHeader(key)`
196
- [here](#removeheaderkey).
197
-
211
+ ##### .routeOptions
212
+ <a id="routeoptions"></a>
198
213
 
214
+ A read-only reference to the route options of the incoming request. It is the
215
+ same object as [`request.routeOptions`](./Request.md).
199
216
 
200
- ### .headers(object)
201
- <a id="headers"></a>
202
-
203
- Sets all the keys of the object as response headers.
204
- [`.header`](#headerkey-value) will be called under the hood.
205
217
  ```js
206
- reply.headers({
207
- 'x-foo': 'foo',
208
- 'x-bar': 'bar'
218
+ fastify.get('/', { config: { foo: 'bar' } }, function (request, reply) {
219
+ reply.send(reply.routeOptions.config.foo) // bar
209
220
  })
210
221
  ```
211
222
 
212
- ### .getHeader(key)
213
- <a id="getHeader"></a>
223
+ ##### .mediaType
224
+ <a id="mediatype"></a>
225
+
226
+ Returns the media type extracted from `Content-Type` header. When `Content-Type`
227
+ header is missing, it will return `undefined`.
214
228
 
215
- Retrieves the value of a previously set header.
216
229
  ```js
217
- reply.header('x-foo', 'foo') // setHeader: key, value
218
- reply.getHeader('x-foo') // 'foo'
230
+ if (reply.mediaType === 'image/gif') {
231
+ const versionBuffer = new Uint8Array(reply.payload, 3, 3)
232
+ reply.header('x-gif-version', new TextDecoder().decode(versionBuffer))
233
+ }
219
234
  ```
220
235
 
221
- ### .getHeaders()
222
- <a id="getHeaders"></a>
236
+ ##### .elapsedTime
237
+ <a id="elapsedTime"></a>
223
238
 
224
- Gets a shallow copy of all current response headers, including those set via the
225
- raw `http.ServerResponse`. Note that headers set via Fastify take precedence
226
- over those set via `http.ServerResponse`.
239
+ Invokes the custom response time getter to calculate the amount of time passed
240
+ since the request was received by Fastify.
227
241
 
228
242
  ```js
229
- reply.header('x-foo', 'foo')
230
- reply.header('x-bar', 'bar')
231
- reply.raw.setHeader('x-foo', 'foo2')
232
- reply.getHeaders() // { 'x-foo': 'foo', 'x-bar': 'bar' }
243
+ const milliseconds = reply.elapsedTime
233
244
  ```
234
245
 
235
- ### .removeHeader(key)
236
- <a id="removeHeader"></a>
246
+ #### Methods
247
+ <a id="methods"></a>
237
248
 
238
- Remove the value of a previously set header.
239
- ```js
240
- reply.header('x-foo', 'foo')
241
- reply.removeHeader('x-foo')
242
- reply.getHeader('x-foo') // undefined
243
- ```
249
+ ##### .send(data)
250
+ <a id="send"></a>
244
251
 
245
- ### .hasHeader(key)
246
- <a id="hasHeader"></a>
252
+ As the name suggests, `.send()` is the function that sends the payload to the
253
+ end user.
247
254
 
248
- Returns a boolean indicating if the specified header has been set.
255
+ ###### Objects
256
+ <a id="send-object"></a>
249
257
 
250
- ### .writeEarlyHints(hints, callback)
251
- <a id="writeEarlyHints"></a>
258
+ As noted above, if you are sending JSON objects, `send` will serialize the
259
+ object with
260
+ [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) if you
261
+ set an output schema, otherwise, `JSON.stringify()` will be used.
262
+ ```js
263
+ fastify.get('/json', options, function (request, reply) {
264
+ reply.send({ hello: 'world' })
265
+ })
266
+ ```
252
267
 
253
- Sends early hints to the client. Early hints allow the client to
254
- start processing resources before the final response is sent.
255
- This can improve performance by allowing the client to preload
256
- or preconnect to resources while the server is still generating the response.
268
+ ###### Strings
269
+ <a id="send-string"></a>
257
270
 
258
- The hints parameter is an object containing the early hint key-value pairs.
271
+ If you pass a string to `send` without a `Content-Type`, it will be sent as
272
+ `text/plain; charset=utf-8`. If you set the `Content-Type` header and pass a
273
+ string to `send`, it will be serialized with the custom serializer if one is
274
+ set, otherwise, it will be sent unmodified.
259
275
 
260
- Example:
276
+ > ℹ️ Note:
277
+ > Even when the `Content-Type` header is set to `application/json`,
278
+ > strings are sent unmodified by default. To serialize a string as JSON, you
279
+ > must set a custom serializer:
261
280
 
262
281
  ```js
263
- reply.writeEarlyHints({
264
- Link: '</styles.css>; rel=preload; as=style'
265
- });
282
+ fastify.get('/json-string', async function (request, reply) {
283
+ reply
284
+ .type('application/json; charset=utf-8')
285
+ .serializer(JSON.stringify)
286
+ .send('Hello') // Returns "Hello" (JSON-encoded string)
287
+ })
288
+ ```
289
+ ```js
290
+ fastify.get('/json', options, function (request, reply) {
291
+ reply.send('plain string')
292
+ })
266
293
  ```
267
294
 
268
- The optional callback parameter is a function that will be called
269
- once the hint is sent or if an error occurs.
270
-
271
- ### .trailer(key, function)
272
- <a id="trailer"></a>
273
-
274
- Sets a response trailer. Trailer is usually used when you need a header that
275
- requires heavy resources to be sent after the `data`, for example,
276
- `Server-Timing` and `Etag`. It can ensure the client receives the response data
277
- as soon as possible.
295
+ ###### Buffers
296
+ <a id="send-buffers"></a>
278
297
 
279
- > ℹ️ Note:
280
- > For HTTP/1 responses, the header `Transfer-Encoding: chunked` will be added
281
- > once you use a trailer. HTTP/2 does not use chunked transfer encoding and
282
- > sends trailers using its native trailing headers support.
298
+ If you are sending a buffer and you have not set a `'Content-Type'` header,
299
+ *send* will set it to `'application/octet-stream'`.
283
300
 
284
- > ℹ️ Note:
285
- > Any error passed to `done` callback will be ignored. If you are interested
286
- > in the error, you can turn on `debug` level logging.
301
+ As noted above, Buffers are considered to be pre-serialized, so they will be
302
+ sent unmodified without response validation.
287
303
 
288
304
  ```js
289
- reply.trailer('server-timing', async function () {
290
- return 'db;dur=53, app;dur=47.2'
291
- })
305
+ const fs = require('node:fs')
292
306
 
293
- const { createHash } = require('node:crypto')
294
- // trailer function also receive two argument
295
- // @param {object} reply fastify reply
296
- // @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
297
- // @param {function} done callback to set trailer value
298
- reply.trailer('content-md5', function(reply, payload, done) {
299
- const hash = createHash('md5')
300
- hash.update(payload)
301
- done(null, hash.digest('hex'))
307
+ fastify.get('/streams', function (request, reply) {
308
+ fs.readFile('some-file', (err, fileBuffer) => {
309
+ reply.send(err || fileBuffer)
310
+ })
302
311
  })
312
+ ```
303
313
 
304
- // when you prefer async-await
305
- reply.trailer('content-md5', async function(reply, payload) {
306
- const hash = createHash('md5')
307
- hash.update(payload)
308
- return hash.digest('hex')
314
+ When using async-await you will need to return or await the reply object:
315
+ ```js
316
+ const fs = require('node:fs')
317
+
318
+ fastify.get('/streams', async function (request, reply) {
319
+ fs.readFile('some-file', (err, fileBuffer) => {
320
+ reply.send(err || fileBuffer)
321
+ })
322
+ return reply
309
323
  })
310
324
  ```
311
325
 
312
- ### .hasTrailer(key)
313
- <a id="hasTrailer"></a>
326
+ ###### TypedArrays
327
+ <a id="send-typedarrays"></a>
314
328
 
315
- Returns a boolean indicating if the specified trailer has been set.
329
+ `send` manages TypedArray like a Buffer, and sets the `'Content-Type'`
330
+ header to `'application/octet-stream'` if not already set.
316
331
 
317
- ### .removeTrailer(key)
318
- <a id="removeTrailer"></a>
332
+ As noted above, TypedArray/Buffers are considered to be pre-serialized, so they
333
+ will be sent unmodified without response validation.
319
334
 
320
- Remove the value of a previously set trailer.
321
335
  ```js
322
- reply.trailer('server-timing', async function () {
323
- return 'db;dur=53, app;dur=47.2'
336
+ const fs = require('node:fs')
337
+
338
+ fastify.get('/streams', function (request, reply) {
339
+ const typedArray = new Uint16Array(10)
340
+ reply.send(typedArray)
324
341
  })
325
- reply.removeTrailer('server-timing')
326
- reply.hasTrailer('server-timing') // false
327
342
  ```
328
343
 
344
+ ###### Streams
345
+ <a id="send-streams"></a>
329
346
 
330
- ### .redirect(dest, [code ,])
331
- <a id="redirect"></a>
347
+ If you are sending a stream and you have not set a `'Content-Type'` header,
348
+ *send* will set it to `'application/octet-stream'`.
332
349
 
333
- Redirects a request to the specified URL, the status code is optional, default
334
- to `302` (if status code is not already set by calling `code`).
350
+ As noted above, streams are considered to be pre-serialized, so they will be
351
+ sent unmodified without response validation.
335
352
 
336
- > ℹ️ Note:
337
- > The input URL must be properly encoded using
338
- > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
339
- > or similar modules such as
340
- > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
341
- > result in a 500 `TypeError` response.
353
+ When sending streams over HTTP/2, Fastify does not change the chunks emitted by
354
+ the stream. If a stream can emit very large chunks, split them in your
355
+ application code, for example by using `fs.createReadStream()` or a transform
356
+ stream that emits smaller chunks.
342
357
 
343
- > ⚠️ Security:
344
- > Encoding alone does **not** prevent open redirects. Neither `encodeURI`
345
- > nor `encodeurl` escapes `/`, so a caller-supplied target such as
346
- > `//evil.com` or `/\evil.com` survives encoding and is resolved by the
347
- > browser as a new host (a protocol-relative, authority-opening
348
- > reference). When `dest` can be influenced by request input
349
- > (`req.query`, `req.body`, `req.params`, cookies, or headers), validate
350
- > that the resolved target is same-origin or on an explicit allowlist
351
- > instead of relying on encoding:
352
- >
353
- > ```js
354
- > const target = new URL(dest, 'https://your-site.example')
355
- > if (target.origin !== 'https://your-site.example') {
356
- > return reply.code(400).send()
357
- > }
358
- > ```
358
+ See special note about error handling for streams in
359
+ [`setErrorHandler`](./Server.md#seterrorhandler).
359
360
 
360
- Example (no `reply.code()` call) sets status code to `302` and redirects to
361
- `/home`
362
361
  ```js
363
- reply.redirect('/home')
364
- ```
362
+ const fs = require('node:fs')
365
363
 
366
- Example (no `reply.code()` call) sets status code to `303` and redirects to
367
- `/home`
364
+ fastify.get('/streams', function (request, reply) {
365
+ const stream = fs.createReadStream('some-file', 'utf8')
366
+ reply.header('Content-Type', 'application/octet-stream')
367
+ reply.send(stream)
368
+ })
369
+ ```
370
+ When using async-await you will need to return or await the reply object:
368
371
  ```js
369
- reply.redirect('/home', 303)
372
+ const fs = require('node:fs')
373
+
374
+ fastify.get('/streams', async function (request, reply) {
375
+ const stream = fs.createReadStream('some-file', 'utf8')
376
+ reply.header('Content-Type', 'application/octet-stream')
377
+ return reply.send(stream)
378
+ })
370
379
  ```
371
380
 
372
- Example (`reply.code()` call) sets status code to `303` and redirects to `/home`
381
+ ###### ReadableStream
382
+ <a id="send-readablestream"></a>
383
+
384
+ `ReadableStream` will be treated as a node stream mentioned above,
385
+ the content is considered to be pre-serialized, so they will be
386
+ sent unmodified without response validation.
387
+
373
388
  ```js
374
- reply.code(303).redirect('/home')
389
+ const fs = require('node:fs')
390
+ const { ReadableStream } = require('node:stream/web')
391
+
392
+ fastify.get('/streams', function (request, reply) {
393
+ const stream = fs.createReadStream('some-file')
394
+ reply.header('Content-Type', 'application/octet-stream')
395
+ reply.send(ReadableStream.from(stream))
396
+ })
375
397
  ```
376
398
 
377
- Example (`reply.code()` call) sets status code to `302` and redirects to `/home`
399
+ ###### Response
400
+ <a id="send-response"></a>
401
+
402
+ `Response` allows to manage the reply payload, status code and
403
+ headers in one place. The payload provided inside `Response` is
404
+ considered to be pre-serialized, so they will be sent unmodified
405
+ without response validation.
406
+
407
+ Please be aware when using `Response`, the status code and headers
408
+ will not directly reflect to `reply.statusCode` and `reply.getHeaders()`.
409
+ Such behavior is based on `Response` only allow `readonly` status
410
+ code and headers. The data is not allow to be bi-direction editing,
411
+ and may confuse when checking the `payload` in `onSend` hooks.
412
+
378
413
  ```js
379
- reply.code(303).redirect('/home', 302)
414
+ const fs = require('node:fs')
415
+ const { ReadableStream } = require('node:stream/web')
416
+
417
+ fastify.get('/streams', function (request, reply) {
418
+ const stream = fs.createReadStream('some-file')
419
+ const readableStream = ReadableStream.from(stream)
420
+ const response = new Response(readableStream, {
421
+ status: 200,
422
+ headers: { 'content-type': 'application/octet-stream' }
423
+ })
424
+ reply.send(response)
425
+ })
380
426
  ```
381
427
 
382
- ### .callNotFound()
383
- <a id="call-not-found"></a>
384
428
 
385
- Invokes the custom not found handler. Note that it will only call `preHandler`
386
- hook specified in [`setNotFoundHandler`](./Server.md#set-not-found-handler).
429
+ ###### Errors
430
+ <a id="errors"></a>
431
+
432
+ If you pass to *send* an object that is an instance of *Error*, Fastify will
433
+ automatically create an error structured as the following:
387
434
 
388
435
  ```js
389
- reply.callNotFound()
436
+ {
437
+ error: String // the HTTP error message
438
+ code: String // the Fastify error code
439
+ message: String // the user error message
440
+ statusCode: Number // the HTTP status code
441
+ }
390
442
  ```
391
443
 
392
- ### .type(contentType)
393
- <a id="type"></a>
444
+ You can add custom properties to the Error object, such as `headers`, that will
445
+ be used to enhance the HTTP response.
394
446
 
395
- Sets the content type for the response. This is a shortcut for
396
- `reply.header('Content-Type', 'the/type')`.
447
+ > ℹ️ Note:
448
+ > If you are passing an error to `send` and the statusCode is less than
449
+ > 400, Fastify will automatically set it at 500.
450
+
451
+ Tip: you can simplify errors by using the
452
+ [`http-errors`](https://www.npmjs.com/package/http-errors) module or
453
+ [`@fastify/sensible`](https://github.com/fastify/fastify-sensible) plugin to
454
+ generate errors:
397
455
 
398
456
  ```js
399
- reply.type('text/html')
457
+ fastify.get('/', function (request, reply) {
458
+ reply.send(httpErrors.Gone())
459
+ })
400
460
  ```
401
- If the `Content-Type` has a JSON subtype, and the charset parameter is not set,
402
- `utf-8` will be used as the charset by default. For other content types, the
403
- charset must be set explicitly.
404
461
 
405
- ### .getSerializationFunction(schema | httpStatus, [contentType])
406
- <a id="getserializationfunction"></a>
462
+ To customize the JSON error output you can do it by:
407
463
 
408
- By calling this function using a provided `schema` or `httpStatus`,
409
- and the optional `contentType`, it will return a `serialization` function
410
- that can be used to serialize diverse inputs. It returns `undefined` if no
411
- serialization function was found using either of the provided inputs.
464
+ - setting a response JSON schema for the status code you need
465
+ - add the additional properties to the `Error` instance
412
466
 
413
- This heavily depends of the `schema#responses` attached to the route, or
414
- the serialization functions compiled by using `compileSerializationSchema`.
467
+ Notice that if the returned status code is not in the response schema list, the
468
+ default behavior will be applied.
415
469
 
416
470
  ```js
417
- const serialize = reply
418
- .getSerializationFunction({
419
- type: 'object',
420
- properties: {
421
- foo: {
422
- type: 'string'
423
- }
424
- }
425
- })
426
- serialize({ foo: 'bar' }) // '{"foo":"bar"}'
471
+ fastify.get('/', {
472
+ schema: {
473
+ response: {
474
+ 501: {
475
+ type: 'object',
476
+ properties: {
477
+ statusCode: { type: 'number' },
478
+ code: { type: 'string' },
479
+ error: { type: 'string' },
480
+ message: { type: 'string' },
481
+ time: { type: 'string' }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }, function (request, reply) {
487
+ const error = new Error('This endpoint has not been implemented')
488
+ error.time = 'it will be implemented in two weeks'
489
+ reply.code(501).send(error)
490
+ })
491
+ ```
427
492
 
428
- // or
493
+ If you want to customize error handling, check out
494
+ [`setErrorHandler`](./Server.md#seterrorhandler) API.
429
495
 
430
- const serialize = reply
431
- .getSerializationFunction(200)
432
- serialize({ foo: 'bar' }) // '{"foo":"bar"}'
496
+ > ℹ️ Note:
497
+ > You are responsible for logging when customizing the error handler.
433
498
 
434
- // or
499
+ API:
435
500
 
436
- const serialize = reply
437
- .getSerializationFunction(200, 'application/json')
438
- serialize({ foo: 'bar' }) // '{"foo":"bar"}'
501
+ ```js
502
+ fastify.setErrorHandler(function (error, request, reply) {
503
+ request.log.warn(error)
504
+ const statusCode = error.statusCode >= 400 ? error.statusCode : 500
505
+ reply
506
+ .code(statusCode)
507
+ .type('text/plain')
508
+ .send(statusCode >= 500 ? 'Internal server error' : error.message)
509
+ })
439
510
  ```
440
511
 
441
- See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
442
- for more information on how to compile serialization schemas.
512
+ Beware that calling `reply.send(error)` in your custom error handler will send
513
+ the error to the default error handler.
514
+ Check out the [Reply Lifecycle](./Lifecycle.md#reply-lifecycle)
515
+ for more information.
443
516
 
444
- ### .compileSerializationSchema(schema, [httpStatus], [contentType])
445
- <a id="compileserializationschema"></a>
517
+ The not found errors generated by the router will use the
518
+ [`setNotFoundHandler`](./Server.md#setnotfoundhandler)
446
519
 
447
- This function will compile a serialization schema and
448
- return a function that can be used to serialize data.
449
- The function returned (a.k.a. _serialization function_) returned is compiled
450
- by using the provided `SerializerCompiler`. Also this is cached by using
451
- a `WeakMap` for reducing compilation calls.
520
+ API:
452
521
 
453
- The optional parameters `httpStatus` and `contentType`, if provided,
454
- are forwarded directly to the `SerializerCompiler`, so it can be used
455
- to compile the serialization function if a custom `SerializerCompiler` is used.
522
+ ```js
523
+ fastify.setNotFoundHandler(function (request, reply) {
524
+ reply
525
+ .code(404)
526
+ .type('text/plain')
527
+ .send('a custom not found')
528
+ })
529
+ ```
456
530
 
457
- This heavily depends of the `schema#responses` attached to the route, or
458
- the serialization functions compiled by using `compileSerializationSchema`.
531
+ ###### Async-Await and Promises
532
+ <a id="async-await-promise"></a>
533
+
534
+ Fastify natively handles promises and supports async-await.
459
535
 
536
+ *Note that in the following examples we are not using reply.send.*
460
537
  ```js
461
- const serialize = reply
462
- .compileSerializationSchema({
463
- type: 'object',
464
- properties: {
465
- foo: {
466
- type: 'string'
467
- }
468
- }
469
- })
470
- serialize({ foo: 'bar' }) // '{"foo":"bar"}'
538
+ const { promisify } = require('node:util')
539
+ const delay = promisify(setTimeout)
471
540
 
472
- // or
541
+ fastify.get('/promises', options, function (request, reply) {
542
+ return delay(200).then(() => { return { hello: 'world' }})
543
+ })
473
544
 
474
- const serialize = reply
475
- .compileSerializationSchema({
476
- type: 'object',
477
- properties: {
478
- foo: {
479
- type: 'string'
480
- }
481
- }
482
- }, 200)
483
- serialize({ foo: 'bar' }) // '{"foo":"bar"}'
545
+ fastify.get('/async-await', options, async function (request, reply) {
546
+ await delay(200)
547
+ return { hello: 'world' }
548
+ })
549
+ ```
484
550
 
485
- // or
551
+ Rejected promises default to a `500` HTTP status code. Reject the promise, or
552
+ `throw` in an `async function`, with an object that has `statusCode` (or
553
+ `status`) and `message` properties to modify the reply.
486
554
 
487
- const serialize = reply
488
- .compileSerializationSchema({
489
- '3xx': {
490
- content: {
491
- 'application/json': {
492
- schema: {
493
- name: { type: 'string' },
494
- phone: { type: 'number' }
495
- }
496
- }
497
- }
498
- }
499
- }, '3xx', 'application/json')
500
- serialize({ name: 'Jone', phone: 201090909090 }) // '{"name":"Jone", "phone":201090909090}'
555
+ ```js
556
+ fastify.get('/teapot', async function (request, reply) {
557
+ const err = new Error()
558
+ err.statusCode = 418
559
+ err.message = 'short and stout'
560
+ throw err
561
+ })
562
+
563
+ fastify.get('/botnet', async function (request, reply) {
564
+ throw { statusCode: 418, message: 'short and stout' }
565
+ // will return to the client the same json
566
+ })
501
567
  ```
502
568
 
503
- Note that you should be careful when using this function, as it will cache
504
- the compiled serialization functions based on the schema provided. If the
505
- schemas provided is mutated or changed, the serialization functions will not
506
- detect that the schema has been altered and for instance it will reuse the
507
- previously compiled serialization function based on the reference of the schema
508
- previously provided.
569
+ If you want to know more please review
570
+ [Routes#async-await](./Routes.md#async-await).
509
571
 
510
- If there's a need to change the properties of a schema, always opt to create
511
- a totally new object, otherwise the implementation won't benefit from the cache
512
- mechanism.
572
+ ###### Type of the final payload
573
+ <a id="payload-type"></a>
513
574
 
514
- :Using the following schema as example:
575
+ The type of the sent payload (after serialization and going through any
576
+ [`onSend` hooks](./Hooks.md#onsend)) must be one of the following types,
577
+ otherwise, an error will be thrown:
578
+
579
+ - `string`
580
+ - `Buffer`
581
+ - `stream`
582
+ - `undefined`
583
+ - `null`
584
+
585
+ ##### .code(statusCode)
586
+ <a id="code"></a>
587
+
588
+ If not set via `reply.code`, the resulting `statusCode` will be `200`.
589
+
590
+ `.status(statusCode)` is an alias of `.code(statusCode)`.
591
+
592
+ ##### .header(key, value)
593
+ <a id="header"></a>
594
+
595
+ Sets a response header. If the value is omitted or undefined, it is coerced to
596
+ `''`.
597
+
598
+ > ℹ️ Note:
599
+ > The header's value must be properly encoded using
600
+ > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
601
+ > or similar modules such as
602
+ > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid characters
603
+ > will result in a 500 `TypeError` response.
604
+
605
+ For more information, see
606
+ [`http.ServerResponse#setHeader`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_response_setheader_name_value).
607
+
608
+ - ###### set-cookie
609
+ <a id="set-cookie"></a>
610
+
611
+ - When sending different values as a cookie with `set-cookie` as the key,
612
+ every value will be sent as a cookie instead of replacing the previous
613
+ value.
614
+
615
+ ```js
616
+ reply.header('set-cookie', 'foo');
617
+ reply.header('set-cookie', 'bar');
618
+ ```
619
+ - The browser will only consider the latest reference of a key for the
620
+ `set-cookie` header. This is done to avoid parsing the `set-cookie` header
621
+ when added to a reply and speeds up the serialization of the reply.
622
+
623
+ - To reset the `set-cookie` header, you need to make an explicit call to
624
+ `reply.removeHeader('set-cookie')`, read more about `.removeHeader(key)`
625
+ [here](#removeheaderkey).
626
+
627
+ ##### .headers(object)
628
+ <a id="headers"></a>
629
+
630
+ Sets all the keys of the object as response headers.
631
+ [`.header`](#headerkey-value) will be called under the hood.
515
632
  ```js
516
- const schema1 = {
517
- type: 'object',
518
- properties: {
519
- foo: {
520
- type: 'string'
521
- }
522
- }
523
- }
633
+ reply.headers({
634
+ 'x-foo': 'foo',
635
+ 'x-bar': 'bar'
636
+ })
524
637
  ```
525
638
 
526
- *Not*
639
+ ##### .type(contentType)
640
+ <a id="type"></a>
641
+
642
+ Sets the content type for the response. This is a shortcut for
643
+ `reply.header('Content-Type', 'the/type')`.
644
+
527
645
  ```js
528
- const serialize = reply.compileSerializationSchema(schema1)
646
+ reply.type('text/html')
647
+ ```
648
+ If the `Content-Type` has a JSON subtype, and the charset parameter is not set,
649
+ `utf-8` will be used as the charset by default. For other content types, the
650
+ charset must be set explicitly.
529
651
 
530
- // Later on...
531
- schema1.properties.foo.type = 'integer'
532
- const newSerialize = reply.compileSerializationSchema(schema1)
652
+ ##### .redirect(dest, [code])
653
+ <a id="redirect"></a>
533
654
 
534
- console.log(newSerialize === serialize) // true
655
+ Redirects a request to the specified URL, the status code is optional, default
656
+ to `302` (if status code is not already set by calling `code`).
657
+
658
+ > ℹ️ Note:
659
+ > The input URL must be properly encoded using
660
+ > [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
661
+ > or similar modules such as
662
+ > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
663
+ > result in a 500 `TypeError` response.
664
+
665
+ > ⚠️ Security:
666
+ > Encoding alone does **not** prevent open redirects. Neither `encodeURI`
667
+ > nor `encodeurl` escapes `/`, so a caller-supplied target such as
668
+ > `//evil.com` or `/\evil.com` survives encoding and is resolved by the
669
+ > browser as a new host (a protocol-relative, authority-opening
670
+ > reference). When `dest` can be influenced by request input
671
+ > (`req.query`, `req.body`, `req.params`, cookies, or headers), validate
672
+ > that the resolved target is same-origin or on an explicit allowlist
673
+ > instead of relying on encoding:
674
+ >
675
+ > ```js
676
+ > const target = new URL(dest, 'https://your-site.example')
677
+ > if (target.origin !== 'https://your-site.example') {
678
+ > return reply.code(400).send()
679
+ > }
680
+ > ```
681
+
682
+ Example (no `reply.code()` call) sets status code to `302` and redirects to
683
+ `/home`
684
+ ```js
685
+ reply.redirect('/home')
535
686
  ```
536
687
 
537
- *Instead*
688
+ Example (no `reply.code()` call) sets status code to `303` and redirects to
689
+ `/home`
538
690
  ```js
539
- const serialize = reply.compileSerializationSchema(schema1)
691
+ reply.redirect('/home', 303)
692
+ ```
540
693
 
541
- // Later on...
542
- const newSchema = Object.assign({}, schema1)
543
- newSchema.properties.foo.type = 'integer'
694
+ Example (`reply.code()` call) sets status code to `303` and redirects to `/home`
695
+ ```js
696
+ reply.code(303).redirect('/home')
697
+ ```
544
698
 
545
- const newSerialize = reply.compileSerializationSchema(newSchema)
699
+ Example (`reply.code()` call) sets status code to `302` and redirects to `/home`
700
+ ```js
701
+ reply.code(303).redirect('/home', 302)
702
+ ```
546
703
 
547
- console.log(newSerialize === serialize) // false
704
+ ##### .getHeader(key)
705
+ <a id="getHeader"></a>
706
+
707
+ Retrieves the value of a previously set header.
708
+ ```js
709
+ reply.header('x-foo', 'foo') // setHeader: key, value
710
+ reply.getHeader('x-foo') // 'foo'
548
711
  ```
549
712
 
550
- ### .serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])
551
- <a id="serializeinput"></a>
713
+ ##### .getHeaders()
714
+ <a id="getHeaders"></a>
552
715
 
553
- This function will serialize the input data based on the provided schema
554
- or HTTP status code. If both are provided the `httpStatus` will take precedence.
716
+ Gets a shallow copy of all current response headers, including those set via the
717
+ raw `http.ServerResponse`. Note that headers set via Fastify take precedence
718
+ over those set via `http.ServerResponse`.
555
719
 
556
- If there is not a serialization function for a given `schema` a new serialization
557
- function will be compiled, forwarding the `httpStatus` and `contentType` if provided.
720
+ ```js
721
+ reply.header('x-foo', 'foo')
722
+ reply.header('x-bar', 'bar')
723
+ reply.raw.setHeader('x-foo', 'foo2')
724
+ reply.getHeaders() // { 'x-foo': 'foo', 'x-bar': 'bar' }
725
+ ```
726
+
727
+ ##### .hasHeader(key)
728
+ <a id="hasHeader"></a>
729
+
730
+ Returns a boolean indicating if the specified header has been set.
731
+
732
+ ##### .removeHeader(key)
733
+ <a id="removeHeader"></a>
558
734
 
735
+ Remove the value of a previously set header.
559
736
  ```js
560
- reply
561
- .serializeInput({ foo: 'bar'}, {
562
- type: 'object',
563
- properties: {
564
- foo: {
565
- type: 'string'
566
- }
567
- }
568
- }) // '{"foo":"bar"}'
737
+ reply.header('x-foo', 'foo')
738
+ reply.removeHeader('x-foo')
739
+ reply.getHeader('x-foo') // undefined
740
+ ```
569
741
 
570
- // or
742
+ ##### .callNotFound()
743
+ <a id="call-not-found"></a>
571
744
 
572
- reply
573
- .serializeInput({ foo: 'bar'}, {
574
- type: 'object',
575
- properties: {
576
- foo: {
577
- type: 'string'
578
- }
579
- }
580
- }, 200) // '{"foo":"bar"}'
745
+ Invokes the custom not found handler. Note that it will only call `preHandler`
746
+ hook specified in [`setNotFoundHandler`](./Server.md#set-not-found-handler).
581
747
 
582
- // or
748
+ ```js
749
+ reply.callNotFound()
750
+ ```
583
751
 
584
- reply
585
- .serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'
752
+ ##### .hijack()
753
+ <a name="hijack"></a>
586
754
 
587
- // or
755
+ Sometimes you might need to halt the execution of the normal request lifecycle
756
+ and handle sending the response manually.
588
757
 
589
- reply
590
- .serializeInput({ name: 'Jone', age: 18 }, '200', 'application/vnd.v1+json') // '{"name": "Jone", "age": 18}'
758
+ To achieve this, Fastify provides the `reply.hijack()` method that can be called
759
+ during the request lifecycle (At any point before `reply.send()` is called), and
760
+ allows you to prevent Fastify from sending the response, and from running the
761
+ remaining hooks (and user handler if the reply was hijacked before).
762
+
763
+ ```js
764
+ app.get('/', (req, reply) => {
765
+ reply.hijack()
766
+ reply.raw.end('hello world')
767
+
768
+ return Promise.resolve('this will be skipped')
769
+ })
591
770
  ```
592
771
 
593
- See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
594
- for more information on how to compile serialization schemas.
772
+ If `reply.raw` is used to send a response back to the user, the `onResponse`
773
+ hooks will still be executed.
595
774
 
596
- ### .serializer(func)
775
+ ##### .serializer(func)
597
776
  <a id="serializer"></a>
598
777
 
599
778
  By default, `.send()` will JSON-serialize any value that is not one of `Buffer`,
@@ -621,415 +800,325 @@ reply
621
800
  See [`.send()`](#send) for more information on sending different types of
622
801
  values.
623
802
 
624
- ### .raw
625
- <a id="raw"></a>
803
+ ##### .serialize(payload)
804
+ <a id="serialize"></a>
626
805
 
627
- This is the
628
- [`http.ServerResponse`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_class_http_serverresponse)
629
- from Node core. Whilst you are using the Fastify `Reply` object, the use of
630
- `Reply.raw` functions is at your own risk as you are skipping all the Fastify
631
- logic of handling the HTTP response. e.g.:
806
+ Serializes the specified payload using the custom serializer set via
807
+ [`.serializer()`](#serializerfunc), if any. Otherwise, it uses the default
808
+ serializer of the route, which takes into account the response schema for the
809
+ current status code. It returns the serialized payload.
632
810
 
633
811
  ```js
634
- app.get('/cookie-2', (req, reply) => {
635
- reply.setCookie('session', 'value', { secure: false }) // this will not be used
636
-
637
- // in this case we are using only the nodejs http server response object
638
- reply.raw.writeHead(200, { 'Content-Type': 'text/plain' })
639
- reply.raw.write('ok')
640
- reply.raw.end()
812
+ fastify.get('/', function (request, reply) {
813
+ const payload = reply.serialize({ hello: 'world' })
814
+ reply.type('application/json').send(payload)
641
815
  })
642
816
  ```
643
- Another example of the misuse of `Reply.raw` is explained in
644
- [Reply](#getheaders).
645
-
646
- ### .sent
647
- <a id="sent"></a>
648
-
649
- As the name suggests, `.sent` is a property to indicate if a response has been
650
- sent via `reply.send()`. It will also be `true` in case `reply.hijack()` was
651
- used.
652
817
 
653
- In case a route handler is defined as an async function or it returns a promise,
654
- it is possible to call `reply.hijack()` to indicate that the automatic
655
- invocation of `reply.send()` once the handler promise resolve should be skipped.
656
- By calling `reply.hijack()`, an application claims full responsibility for the
657
- low-level request and response. Moreover, hooks will not be invoked.
818
+ ##### .trailer(key, function)
819
+ <a id="trailer"></a>
658
820
 
659
- ### .hijack()
660
- <a name="hijack"></a>
821
+ Sets a response trailer. Trailer is usually used when you need a header that
822
+ requires heavy resources to be sent after the `data`, for example,
823
+ `Server-Timing` and `Etag`. It can ensure the client receives the response data
824
+ as soon as possible.
661
825
 
662
- Sometimes you might need to halt the execution of the normal request lifecycle
663
- and handle sending the response manually.
826
+ > ℹ️ Note:
827
+ > For HTTP/1 responses, the header `Transfer-Encoding: chunked` will be added
828
+ > once you use a trailer. HTTP/2 does not use chunked transfer encoding and
829
+ > sends trailers using its native trailing headers support.
664
830
 
665
- To achieve this, Fastify provides the `reply.hijack()` method that can be called
666
- during the request lifecycle (At any point before `reply.send()` is called), and
667
- allows you to prevent Fastify from sending the response, and from running the
668
- remaining hooks (and user handler if the reply was hijacked before).
831
+ > ℹ️ Note:
832
+ > Any error passed to `done` callback will be ignored. If you are interested
833
+ > in the error, you can turn on `debug` level logging.
669
834
 
670
835
  ```js
671
- app.get('/', (req, reply) => {
672
- reply.hijack()
673
- reply.raw.end('hello world')
674
-
675
- return Promise.resolve('this will be skipped')
836
+ reply.trailer('server-timing', async function () {
837
+ return 'db;dur=53, app;dur=47.2'
676
838
  })
677
- ```
678
-
679
- If `reply.raw` is used to send a response back to the user, the `onResponse`
680
- hooks will still be executed.
681
839
 
682
- ### .send(data)
683
- <a id="send"></a>
684
-
685
- As the name suggests, `.send()` is the function that sends the payload to the
686
- end user.
687
-
688
- #### Objects
689
- <a id="send-object"></a>
840
+ const { createHash } = require('node:crypto')
841
+ // trailer function also receive two argument
842
+ // @param {object} reply fastify reply
843
+ // @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
844
+ // @param {function} done callback to set trailer value
845
+ reply.trailer('content-md5', function(reply, payload, done) {
846
+ const hash = createHash('md5')
847
+ hash.update(payload)
848
+ done(null, hash.digest('hex'))
849
+ })
690
850
 
691
- As noted above, if you are sending JSON objects, `send` will serialize the
692
- object with
693
- [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) if you
694
- set an output schema, otherwise, `JSON.stringify()` will be used.
695
- ```js
696
- fastify.get('/json', options, function (request, reply) {
697
- reply.send({ hello: 'world' })
851
+ // when you prefer async-await
852
+ reply.trailer('content-md5', async function(reply, payload) {
853
+ const hash = createHash('md5')
854
+ hash.update(payload)
855
+ return hash.digest('hex')
698
856
  })
699
857
  ```
700
858
 
701
- #### Strings
702
- <a id="send-string"></a>
859
+ ##### .hasTrailer(key)
860
+ <a id="hasTrailer"></a>
703
861
 
704
- If you pass a string to `send` without a `Content-Type`, it will be sent as
705
- `text/plain; charset=utf-8`. If you set the `Content-Type` header and pass a
706
- string to `send`, it will be serialized with the custom serializer if one is
707
- set, otherwise, it will be sent unmodified.
862
+ Returns a boolean indicating if the specified trailer has been set.
708
863
 
709
- > ℹ️ Note:
710
- > Even when the `Content-Type` header is set to `application/json`,
711
- > strings are sent unmodified by default. To serialize a string as JSON, you
712
- > must set a custom serializer:
864
+ ##### .removeTrailer(key)
865
+ <a id="removeTrailer"></a>
713
866
 
867
+ Remove the value of a previously set trailer.
714
868
  ```js
715
- fastify.get('/json-string', async function (request, reply) {
716
- reply
717
- .type('application/json; charset=utf-8')
718
- .serializer(JSON.stringify)
719
- .send('Hello') // Returns "Hello" (JSON-encoded string)
720
- })
721
- ```
722
- ```js
723
- fastify.get('/json', options, function (request, reply) {
724
- reply.send('plain string')
869
+ reply.trailer('server-timing', async function () {
870
+ return 'db;dur=53, app;dur=47.2'
725
871
  })
872
+ reply.removeTrailer('server-timing')
873
+ reply.hasTrailer('server-timing') // false
726
874
  ```
727
875
 
728
- #### Streams
729
- <a id="send-streams"></a>
730
-
731
- If you are sending a stream and you have not set a `'Content-Type'` header,
732
- *send* will set it to `'application/octet-stream'`.
733
-
734
- As noted above, streams are considered to be pre-serialized, so they will be
735
- sent unmodified without response validation.
876
+ ##### .writeEarlyHints(hints, callback)
877
+ <a id="writeEarlyHints"></a>
736
878
 
737
- When sending streams over HTTP/2, Fastify does not change the chunks emitted by
738
- the stream. If a stream can emit very large chunks, split them in your
739
- application code, for example by using `fs.createReadStream()` or a transform
740
- stream that emits smaller chunks.
879
+ Sends early hints to the client. Early hints allow the client to
880
+ start processing resources before the final response is sent.
881
+ This can improve performance by allowing the client to preload
882
+ or preconnect to resources while the server is still generating the response.
741
883
 
742
- See special note about error handling for streams in
743
- [`setErrorHandler`](./Server.md#seterrorhandler).
884
+ The hints parameter is an object containing the early hint key-value pairs.
744
885
 
745
- ```js
746
- const fs = require('node:fs')
886
+ Example:
747
887
 
748
- fastify.get('/streams', function (request, reply) {
749
- const stream = fs.createReadStream('some-file', 'utf8')
750
- reply.header('Content-Type', 'application/octet-stream')
751
- reply.send(stream)
752
- })
753
- ```
754
- When using async-await you will need to return or await the reply object:
755
888
  ```js
756
- const fs = require('node:fs')
757
-
758
- fastify.get('/streams', async function (request, reply) {
759
- const stream = fs.createReadStream('some-file', 'utf8')
760
- reply.header('Content-Type', 'application/octet-stream')
761
- return reply.send(stream)
762
- })
889
+ reply.writeEarlyHints({
890
+ Link: '</styles.css>; rel=preload; as=style'
891
+ });
763
892
  ```
764
893
 
765
- #### Buffers
766
- <a id="send-buffers"></a>
894
+ The optional callback parameter is a function that will be called
895
+ once the hint is sent or if an error occurs.
767
896
 
768
- If you are sending a buffer and you have not set a `'Content-Type'` header,
769
- *send* will set it to `'application/octet-stream'`.
897
+ ##### .then(fulfilled, rejected)
898
+ <a id="then"></a>
770
899
 
771
- As noted above, Buffers are considered to be pre-serialized, so they will be
772
- sent unmodified without response validation.
900
+ As the name suggests, a `Reply` object can be awaited upon, i.e. `await reply`
901
+ will wait until the reply is sent. The `await` syntax calls the `reply.then()`.
773
902
 
774
- ```js
775
- const fs = require('node:fs')
903
+ `reply.then(fulfilled, rejected)` accepts two parameters:
776
904
 
777
- fastify.get('/streams', function (request, reply) {
778
- fs.readFile('some-file', (err, fileBuffer) => {
779
- reply.send(err || fileBuffer)
780
- })
781
- })
782
- ```
905
+ - `fulfilled` will be called when a response has been fully sent,
906
+ - `rejected` will be called if the underlying stream had an error, e.g. the
907
+ socket has been destroyed.
783
908
 
784
- When using async-await you will need to return or await the reply object:
785
- ```js
786
- const fs = require('node:fs')
909
+ For more details, see:
787
910
 
788
- fastify.get('/streams', async function (request, reply) {
789
- fs.readFile('some-file', (err, fileBuffer) => {
790
- reply.send(err || fileBuffer)
791
- })
792
- return reply
793
- })
794
- ```
911
+ - https://github.com/fastify/fastify/issues/1864 for the discussion about this
912
+ feature
913
+ - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
914
+ for the signature
795
915
 
796
- #### TypedArrays
797
- <a id="send-typedarrays"></a>
916
+ ##### .getDecorator(name)
798
917
 
799
- `send` manages TypedArray like a Buffer, and sets the `'Content-Type'`
800
- header to `'application/octet-stream'` if not already set.
801
-
802
- As noted above, TypedArray/Buffers are considered to be pre-serialized, so they
803
- will be sent unmodified without response validation.
918
+ Returns the value of a reply decorator declared with
919
+ [`decorateReply`](./Decorators.md#decoratereplyname-value-dependencies). If the
920
+ decorator is a function, it is returned bound to the reply. It throws an
921
+ `FST_ERR_DEC_UNDECLARED` error if the decorator has not been declared. See
922
+ [`getDecorator`](./Decorators.md#getdecoratorname) for more details.
804
923
 
805
924
  ```js
806
- const fs = require('node:fs')
925
+ fastify.decorateReply('helper', function () {
926
+ return this.statusCode
927
+ })
807
928
 
808
- fastify.get('/streams', function (request, reply) {
809
- const typedArray = new Uint16Array(10)
810
- reply.send(typedArray)
929
+ fastify.get('/', function (request, reply) {
930
+ const helper = reply.getDecorator('helper')
931
+ reply.code(201).send({ statusCode: helper() }) // { statusCode: 201 }
811
932
  })
812
933
  ```
813
934
 
814
- #### ReadableStream
815
- <a id="send-readablestream"></a>
935
+ ##### .getSerializationFunction(schema | httpStatus, [contentType])
936
+ <a id="getserializationfunction"></a>
816
937
 
817
- `ReadableStream` will be treated as a node stream mentioned above,
818
- the content is considered to be pre-serialized, so they will be
819
- sent unmodified without response validation.
938
+ By calling this function using a provided `schema` or `httpStatus`,
939
+ and the optional `contentType`, it will return a `serialization` function
940
+ that can be used to serialize diverse inputs. It returns `undefined` if no
941
+ serialization function was found using either of the provided inputs.
942
+
943
+ This heavily depends of the `schema#responses` attached to the route, or
944
+ the serialization functions compiled by using `compileSerializationSchema`.
820
945
 
821
946
  ```js
822
- const fs = require('node:fs')
823
- const { ReadableStream } = require('node:stream/web')
947
+ const serialize = reply
948
+ .getSerializationFunction({
949
+ type: 'object',
950
+ properties: {
951
+ foo: {
952
+ type: 'string'
953
+ }
954
+ }
955
+ })
956
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
824
957
 
825
- fastify.get('/streams', function (request, reply) {
826
- const stream = fs.createReadStream('some-file')
827
- reply.header('Content-Type', 'application/octet-stream')
828
- reply.send(ReadableStream.from(stream))
829
- })
830
- ```
958
+ // or
831
959
 
832
- #### Response
833
- <a id="send-response"></a>
960
+ const serialize = reply
961
+ .getSerializationFunction(200)
962
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
834
963
 
835
- `Response` allows to manage the reply payload, status code and
836
- headers in one place. The payload provided inside `Response` is
837
- considered to be pre-serialized, so they will be sent unmodified
838
- without response validation.
964
+ // or
839
965
 
840
- Please be aware when using `Response`, the status code and headers
841
- will not directly reflect to `reply.statusCode` and `reply.getHeaders()`.
842
- Such behavior is based on `Response` only allow `readonly` status
843
- code and headers. The data is not allow to be bi-direction editing,
844
- and may confuse when checking the `payload` in `onSend` hooks.
966
+ const serialize = reply
967
+ .getSerializationFunction(200, 'application/json')
968
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
969
+ ```
845
970
 
846
- ```js
847
- const fs = require('node:fs')
848
- const { ReadableStream } = require('node:stream/web')
971
+ See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
972
+ for more information on how to compile serialization schemas.
849
973
 
850
- fastify.get('/streams', function (request, reply) {
851
- const stream = fs.createReadStream('some-file')
852
- const readableStream = ReadableStream.from(stream)
853
- const response = new Response(readableStream, {
854
- status: 200,
855
- headers: { 'content-type': 'application/octet-stream' }
856
- })
857
- reply.send(response)
858
- })
859
- ```
974
+ ##### .compileSerializationSchema(schema, [httpStatus], [contentType])
975
+ <a id="compileserializationschema"></a>
860
976
 
977
+ This function will compile a serialization schema and
978
+ return a function that can be used to serialize data.
979
+ The function returned (a.k.a. _serialization function_) returned is compiled
980
+ by using the provided `SerializerCompiler`. Also this is cached by using
981
+ a `WeakMap` for reducing compilation calls.
861
982
 
862
- #### Errors
863
- <a id="errors"></a>
983
+ The optional parameters `httpStatus` and `contentType`, if provided,
984
+ are forwarded directly to the `SerializerCompiler`, so it can be used
985
+ to compile the serialization function if a custom `SerializerCompiler` is used.
864
986
 
865
- If you pass to *send* an object that is an instance of *Error*, Fastify will
866
- automatically create an error structured as the following:
987
+ This heavily depends of the `schema#responses` attached to the route, or
988
+ the serialization functions compiled by using `compileSerializationSchema`.
867
989
 
868
990
  ```js
869
- {
870
- error: String // the HTTP error message
871
- code: String // the Fastify error code
872
- message: String // the user error message
873
- statusCode: Number // the HTTP status code
874
- }
875
- ```
991
+ const serialize = reply
992
+ .compileSerializationSchema({
993
+ type: 'object',
994
+ properties: {
995
+ foo: {
996
+ type: 'string'
997
+ }
998
+ }
999
+ })
1000
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
876
1001
 
877
- You can add custom properties to the Error object, such as `headers`, that will
878
- be used to enhance the HTTP response.
1002
+ // or
879
1003
 
880
- > ℹ️ Note:
881
- > If you are passing an error to `send` and the statusCode is less than
882
- > 400, Fastify will automatically set it at 500.
1004
+ const serialize = reply
1005
+ .compileSerializationSchema({
1006
+ type: 'object',
1007
+ properties: {
1008
+ foo: {
1009
+ type: 'string'
1010
+ }
1011
+ }
1012
+ }, 200)
1013
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
883
1014
 
884
- Tip: you can simplify errors by using the
885
- [`http-errors`](https://www.npmjs.com/package/http-errors) module or
886
- [`@fastify/sensible`](https://github.com/fastify/fastify-sensible) plugin to
887
- generate errors:
1015
+ // or
888
1016
 
889
- ```js
890
- fastify.get('/', function (request, reply) {
891
- reply.send(httpErrors.Gone())
892
- })
1017
+ const serialize = reply
1018
+ .compileSerializationSchema({
1019
+ '3xx': {
1020
+ content: {
1021
+ 'application/json': {
1022
+ schema: {
1023
+ name: { type: 'string' },
1024
+ phone: { type: 'number' }
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+ }, '3xx', 'application/json')
1030
+ serialize({ name: 'Jone', phone: 201090909090 }) // '{"name":"Jone", "phone":201090909090}'
893
1031
  ```
894
1032
 
895
- To customize the JSON error output you can do it by:
896
-
897
- - setting a response JSON schema for the status code you need
898
- - add the additional properties to the `Error` instance
1033
+ Note that you should be careful when using this function, as it will cache
1034
+ the compiled serialization functions based on the schema provided. If the
1035
+ schemas provided is mutated or changed, the serialization functions will not
1036
+ detect that the schema has been altered and for instance it will reuse the
1037
+ previously compiled serialization function based on the reference of the schema
1038
+ previously provided.
899
1039
 
900
- Notice that if the returned status code is not in the response schema list, the
901
- default behavior will be applied.
1040
+ If there's a need to change the properties of a schema, always opt to create
1041
+ a totally new object, otherwise the implementation won't benefit from the cache
1042
+ mechanism.
902
1043
 
1044
+ :Using the following schema as example:
903
1045
  ```js
904
- fastify.get('/', {
905
- schema: {
906
- response: {
907
- 501: {
908
- type: 'object',
909
- properties: {
910
- statusCode: { type: 'number' },
911
- code: { type: 'string' },
912
- error: { type: 'string' },
913
- message: { type: 'string' },
914
- time: { type: 'string' }
915
- }
916
- }
1046
+ const schema1 = {
1047
+ type: 'object',
1048
+ properties: {
1049
+ foo: {
1050
+ type: 'string'
917
1051
  }
918
1052
  }
919
- }, function (request, reply) {
920
- const error = new Error('This endpoint has not been implemented')
921
- error.time = 'it will be implemented in two weeks'
922
- reply.code(501).send(error)
923
- })
1053
+ }
924
1054
  ```
925
1055
 
926
- If you want to customize error handling, check out
927
- [`setErrorHandler`](./Server.md#seterrorhandler) API.
928
-
929
- > ℹ️ Note:
930
- > You are responsible for logging when customizing the error handler.
931
-
932
- API:
933
-
1056
+ *Not*
934
1057
  ```js
935
- fastify.setErrorHandler(function (error, request, reply) {
936
- request.log.warn(error)
937
- const statusCode = error.statusCode >= 400 ? error.statusCode : 500
938
- reply
939
- .code(statusCode)
940
- .type('text/plain')
941
- .send(statusCode >= 500 ? 'Internal server error' : error.message)
942
- })
943
- ```
944
-
945
- Beware that calling `reply.send(error)` in your custom error handler will send
946
- the error to the default error handler.
947
- Check out the [Reply Lifecycle](./Lifecycle.md#reply-lifecycle)
948
- for more information.
949
-
950
- The not found errors generated by the router will use the
951
- [`setNotFoundHandler`](./Server.md#setnotfoundhandler)
1058
+ const serialize = reply.compileSerializationSchema(schema1)
952
1059
 
953
- API:
1060
+ // Later on...
1061
+ schema1.properties.foo.type = 'integer'
1062
+ const newSerialize = reply.compileSerializationSchema(schema1)
954
1063
 
955
- ```js
956
- fastify.setNotFoundHandler(function (request, reply) {
957
- reply
958
- .code(404)
959
- .type('text/plain')
960
- .send('a custom not found')
961
- })
1064
+ console.log(newSerialize === serialize) // true
962
1065
  ```
963
1066
 
964
- #### Type of the final payload
965
- <a id="payload-type"></a>
966
-
967
- The type of the sent payload (after serialization and going through any
968
- [`onSend` hooks](./Hooks.md#onsend)) must be one of the following types,
969
- otherwise, an error will be thrown:
970
-
971
- - `string`
972
- - `Buffer`
973
- - `stream`
974
- - `undefined`
975
- - `null`
1067
+ *Instead*
1068
+ ```js
1069
+ const serialize = reply.compileSerializationSchema(schema1)
976
1070
 
977
- #### Async-Await and Promises
978
- <a id="async-await-promise"></a>
1071
+ // Later on...
1072
+ const newSchema = Object.assign({}, schema1)
1073
+ newSchema.properties.foo.type = 'integer'
979
1074
 
980
- Fastify natively handles promises and supports async-await.
1075
+ const newSerialize = reply.compileSerializationSchema(newSchema)
981
1076
 
982
- *Note that in the following examples we are not using reply.send.*
983
- ```js
984
- const { promisify } = require('node:util')
985
- const delay = promisify(setTimeout)
1077
+ console.log(newSerialize === serialize) // false
1078
+ ```
986
1079
 
987
- fastify.get('/promises', options, function (request, reply) {
988
- return delay(200).then(() => { return { hello: 'world' }})
989
- })
1080
+ ##### .serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])
1081
+ <a id="serializeinput"></a>
990
1082
 
991
- fastify.get('/async-await', options, async function (request, reply) {
992
- await delay(200)
993
- return { hello: 'world' }
994
- })
995
- ```
1083
+ This function will serialize the input data based on the provided schema
1084
+ or HTTP status code. If both are provided the `httpStatus` will take precedence.
996
1085
 
997
- Rejected promises default to a `500` HTTP status code. Reject the promise, or
998
- `throw` in an `async function`, with an object that has `statusCode` (or
999
- `status`) and `message` properties to modify the reply.
1086
+ If there is not a serialization function for a given `schema` a new serialization
1087
+ function will be compiled, forwarding the `httpStatus` and `contentType` if provided.
1000
1088
 
1001
1089
  ```js
1002
- fastify.get('/teapot', async function (request, reply) {
1003
- const err = new Error()
1004
- err.statusCode = 418
1005
- err.message = 'short and stout'
1006
- throw err
1007
- })
1008
-
1009
- fastify.get('/botnet', async function (request, reply) {
1010
- throw { statusCode: 418, message: 'short and stout' }
1011
- // will return to the client the same json
1012
- })
1013
- ```
1090
+ reply
1091
+ .serializeInput({ foo: 'bar'}, {
1092
+ type: 'object',
1093
+ properties: {
1094
+ foo: {
1095
+ type: 'string'
1096
+ }
1097
+ }
1098
+ }) // '{"foo":"bar"}'
1014
1099
 
1015
- If you want to know more please review
1016
- [Routes#async-await](./Routes.md#async-await).
1100
+ // or
1017
1101
 
1018
- ### .then(fulfilled, rejected)
1019
- <a id="then"></a>
1102
+ reply
1103
+ .serializeInput({ foo: 'bar'}, {
1104
+ type: 'object',
1105
+ properties: {
1106
+ foo: {
1107
+ type: 'string'
1108
+ }
1109
+ }
1110
+ }, 200) // '{"foo":"bar"}'
1020
1111
 
1021
- As the name suggests, a `Reply` object can be awaited upon, i.e. `await reply`
1022
- will wait until the reply is sent. The `await` syntax calls the `reply.then()`.
1112
+ // or
1023
1113
 
1024
- `reply.then(fulfilled, rejected)` accepts two parameters:
1114
+ reply
1115
+ .serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'
1025
1116
 
1026
- - `fulfilled` will be called when a response has been fully sent,
1027
- - `rejected` will be called if the underlying stream had an error, e.g. the
1028
- socket has been destroyed.
1117
+ // or
1029
1118
 
1030
- For more details, see:
1119
+ reply
1120
+ .serializeInput({ name: 'Jone', age: 18 }, '200', 'application/vnd.v1+json') // '{"name": "Jone", "age": 18}'
1121
+ ```
1031
1122
 
1032
- - https://github.com/fastify/fastify/issues/1864 for the discussion about this
1033
- feature
1034
- - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
1035
- for the signature
1123
+ See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
1124
+ for more information on how to compile serialization schemas.