fastify 3.20.1 → 3.20.2
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.
- package/docs/Ecosystem.md +2 -1
- package/docs/Getting-Started.md +1 -1
- package/docs/Request.md +18 -1
- package/docs/TypeScript.md +2 -2
- package/fastify.js +7 -5
- package/lib/request.js +6 -0
- package/package.json +1 -1
- package/test/request-error.test.js +72 -0
- package/test/schema-special-usage.test.js +32 -0
package/docs/Ecosystem.md
CHANGED
|
@@ -44,6 +44,7 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
|
|
|
44
44
|
- [`fastify-routes`](https://github.com/fastify/fastify-routes) Plugin that provides a `Map` of routes.
|
|
45
45
|
- [`fastify-schedule`](https://github.com/fastify/fastify-schedule) Plugin for scheduling periodic jobs, based on [toad-scheduler](https://github.com/kibertoad/toad-scheduler).
|
|
46
46
|
- [`fastify-sensible`](https://github.com/fastify/fastify-sensible) Defaults for Fastify that everyone can agree on. It adds some useful decorators such as HTTP errors and assertions, but also more request and reply methods.
|
|
47
|
+
- [`@fastify/session`](https://github.com/fastify/session) a session plugin for Fastify.
|
|
47
48
|
- [`fastify-static`](https://github.com/fastify/fastify-static) Plugin for serving static files as fast as possible.
|
|
48
49
|
- [`fastify-swagger`](https://github.com/fastify/fastify-swagger) Plugin for serving Swagger/OpenAPI documentation for Fastify, supporting dynamic generation.
|
|
49
50
|
- [`fastify-websocket`](https://github.com/fastify/fastify-websocket) WebSocket support for Fastify. Built upon [websocket-stream](https://github.com/maxogden/websocket-stream).
|
|
@@ -69,6 +70,7 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
|
|
|
69
70
|
- [`arecibo`](https://github.com/nucleode/arecibo) Fastify ping responder for Kubernetes Liveness and Readiness Probes.
|
|
70
71
|
- [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware for CLS-based request ID generation. An out-of-the-box solution for adding request IDs into your logs.
|
|
71
72
|
- [`fastify-405`](https://github.com/Eomm/fastify-405) Fastify plugin that adds 405 HTTP status to your routes
|
|
73
|
+
- [`fastify-allow`](https://github.com/mattbishop/fastify-allow) Fastify plugin that automatically adds an Allow header to responses with routes. Also sends 405 responses for routes that have a handler but not for the request's method.
|
|
72
74
|
- [`fastify-amqp`](https://github.com/RafaelGSS/fastify-amqp) Fastify AMQP connection plugin, to use with RabbitMQ or another connector. Just a wrapper to [`amqplib`](https://github.com/squaremo/amqp.node).
|
|
73
75
|
- [`fastify-angular-universal`](https://github.com/exequiel09/fastify-angular-universal) Angular server-side rendering support using [`@angular/platform-server`](https://github.com/angular/angular/tree/master/packages/platform-server) for Fastify
|
|
74
76
|
- [`fastify-api-key`](https://github.com/arkerone/fastify-api-key) Fastify plugin to authenticate HTTP requests based on api key and signature
|
|
@@ -177,7 +179,6 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
|
|
|
177
179
|
- [`fastify-sentry`](https://github.com/alex-ppg/fastify-sentry) Fastify plugin to add the Sentry SDK error handler to requests.
|
|
178
180
|
- [`fastify-sequelize`](https://github.com/lyquocnam/fastify-sequelize) Fastify plugin work with Sequelize (adapter for NodeJS -> Sqlite, Mysql, Mssql, Postgres).
|
|
179
181
|
- [`fastify-server-session`](https://github.com/jsumners/fastify-server-session) A session plugin with support for arbitrary backing caches via `fastify-caching`.
|
|
180
|
-
- [`fastify-session`](https://github.com/SerayaEryn/fastify-session) a session plugin for Fastify.
|
|
181
182
|
- [`fastify-slonik`](https://github.com/Unbuttun/fastify-slonik) Fastify Slonik plugin, with this you can use slonik in every part of your server.
|
|
182
183
|
- [`fastify-soap-client`](https://github.com/fastify/fastify-soap-client) a SOAP client plugin for Fastify.
|
|
183
184
|
- [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io) a Socket.io plugin for Fastify.
|
package/docs/Getting-Started.md
CHANGED
|
@@ -200,7 +200,7 @@ As you can see, we used `register` for both the database connector and the regis
|
|
|
200
200
|
This is one of the best features of Fastify, it will load your plugins in the same order you declare them, and it will load the next plugin only once the current one has been loaded. In this way, we can register the database connector in the first plugin and use it in the second *(read [here](Plugins.md#handle-the-scope) to understand how to handle the scope of a plugin)*.
|
|
201
201
|
Plugin loading starts when you call `fastify.listen()`, `fastify.inject()` or `fastify.ready()`
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
The MongoDB plugin uses the `decorate` API to add custom objects to the Fastify instance, making them available for use everywhere. Use of this API is encouraged to facilitate easy code reuse and to decrease code or logic duplication.
|
|
204
204
|
|
|
205
205
|
To dig deeper into how Fastify plugins work, how to develop new plugins, and for details on how to use the whole Fastify API to deal with the complexity of asynchronously bootstrapping an application, read [the hitchhiker's guide to plugins](Plugins-Guide.md).
|
|
206
206
|
|
package/docs/Request.md
CHANGED
|
@@ -6,7 +6,7 @@ Request is a core Fastify object containing the following fields:
|
|
|
6
6
|
- `query` - the parsed querystring
|
|
7
7
|
- `body` - the body
|
|
8
8
|
- `params` - the params matching the URL
|
|
9
|
-
- `headers` - the headers
|
|
9
|
+
- [`headers`](#headers) - the headers getter and setter
|
|
10
10
|
- `raw` - the incoming HTTP request from Node core
|
|
11
11
|
- `req` *(deprecated, use `.raw` instead)* - the incoming HTTP request from Node core
|
|
12
12
|
- `server` - The Fastify server instance, scoped to the current [encapsulation context](Encapsulation.md)
|
|
@@ -26,6 +26,20 @@ Request is a core Fastify object containing the following fields:
|
|
|
26
26
|
- `context` - A Fastify internal object. You should not use it directly or modify it. It is usefull to access one special key:
|
|
27
27
|
- `context.config` - The route [`config`](Routes.md#routes-config) object.
|
|
28
28
|
|
|
29
|
+
### Headers
|
|
30
|
+
|
|
31
|
+
The `request.headers` is a getter that return an Object with the headers of the incoming request.
|
|
32
|
+
You can set custom headers like this:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
request.headers = {
|
|
36
|
+
'foo': 'bar',
|
|
37
|
+
'baz': 'qux'
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This operation will add to the request headers the new values that can be read calling `request.headers.bar`.
|
|
42
|
+
Moreover, you can still access the standard request's headers with the `request.raw.headers` property.
|
|
29
43
|
|
|
30
44
|
```js
|
|
31
45
|
fastify.post('/:params', options, function (request, reply) {
|
|
@@ -40,6 +54,9 @@ fastify.post('/:params', options, function (request, reply) {
|
|
|
40
54
|
console.log(request.ips)
|
|
41
55
|
console.log(request.hostname)
|
|
42
56
|
console.log(request.protocol)
|
|
57
|
+
console.log(request.url)
|
|
58
|
+
console.log(request.routerMethod)
|
|
59
|
+
console.log(request.routerPath)
|
|
43
60
|
request.log.info('some info')
|
|
44
61
|
})
|
|
45
62
|
```
|
package/docs/TypeScript.md
CHANGED
|
@@ -85,7 +85,7 @@ The type system heavily relies on generic properties to provide the most accurat
|
|
|
85
85
|
'h-Custom': string;
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
|
-
3. Using the two interfaces, define a new API route and pass them as generics. The shorthand route methods (i.e. `.get`) accept a generic object `
|
|
88
|
+
3. Using the two interfaces, define a new API route and pass them as generics. The shorthand route methods (i.e. `.get`) accept a generic object `RouteGenericInterface` containing five named properties: `Body`, `Querystring`, `Params`, `Headers` and `Reply`. The interfaces `Body`, `Querystring`, `Params` and `Headers` will be passed down through the route method into the route method handler `request` instance and the `Reply` interface to the `reply` instance.
|
|
89
89
|
```typescript
|
|
90
90
|
server.get<{
|
|
91
91
|
Querystring: IQuerystring,
|
|
@@ -599,7 +599,7 @@ The Fastify API is powered by the `fastify()` method. In JavaScript you would im
|
|
|
599
599
|
const f = fastify()
|
|
600
600
|
f.listen(8080, () => { console.log('running') })
|
|
601
601
|
```
|
|
602
|
-
- Destructuring is
|
|
602
|
+
- Destructuring is supported and will resolve types properly
|
|
603
603
|
```typescript
|
|
604
604
|
const { fastify } = require('fastify')
|
|
605
605
|
|
package/fastify.js
CHANGED
|
@@ -541,9 +541,8 @@ function fastify (options) {
|
|
|
541
541
|
|
|
542
542
|
function defaultClientErrorHandler (err, socket) {
|
|
543
543
|
// In case of a connection reset, the socket has been destroyed and there is nothing that needs to be done.
|
|
544
|
-
// https://
|
|
545
|
-
|
|
546
|
-
if (err.code === 'ECONNRESET') {
|
|
544
|
+
// https://nodejs.org/api/http.html#http_event_clienterror
|
|
545
|
+
if (err.code === 'ECONNRESET' || socket.destroyed) {
|
|
547
546
|
return
|
|
548
547
|
}
|
|
549
548
|
|
|
@@ -557,11 +556,14 @@ function fastify (options) {
|
|
|
557
556
|
// In the vast majority of cases, it's a network error and/or some
|
|
558
557
|
// config issue on the load balancer side.
|
|
559
558
|
this.log.trace({ err }, 'client error')
|
|
559
|
+
// Copying standard node behaviour
|
|
560
|
+
// https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
|
|
560
561
|
|
|
561
562
|
// If the socket is not writable, there is no reason to try to send data.
|
|
562
|
-
if (socket.writable) {
|
|
563
|
-
socket.
|
|
563
|
+
if (socket.writable && socket.bytesWritten === 0) {
|
|
564
|
+
socket.write(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
|
|
564
565
|
}
|
|
566
|
+
socket.destroy(err)
|
|
565
567
|
}
|
|
566
568
|
|
|
567
569
|
// If the router does not match any route, every request will land here
|
package/lib/request.js
CHANGED
|
@@ -162,7 +162,13 @@ Object.defineProperties(Request.prototype, {
|
|
|
162
162
|
},
|
|
163
163
|
headers: {
|
|
164
164
|
get () {
|
|
165
|
+
if (this.additionalHeaders) {
|
|
166
|
+
return Object.assign({}, this.raw.headers, this.additionalHeaders)
|
|
167
|
+
}
|
|
165
168
|
return this.raw.headers
|
|
169
|
+
},
|
|
170
|
+
set (headers) {
|
|
171
|
+
this.additionalHeaders = headers
|
|
166
172
|
}
|
|
167
173
|
},
|
|
168
174
|
server: {
|
package/package.json
CHANGED
|
@@ -127,6 +127,78 @@ test('default clientError handler ignores ECONNRESET', t => {
|
|
|
127
127
|
})
|
|
128
128
|
})
|
|
129
129
|
|
|
130
|
+
test('default clientError handler ignores sockets in destroyed state', t => {
|
|
131
|
+
t.plan(1)
|
|
132
|
+
|
|
133
|
+
const fastify = Fastify({
|
|
134
|
+
bodyLimit: 1,
|
|
135
|
+
keepAliveTimeout: 100,
|
|
136
|
+
logger: {
|
|
137
|
+
level: 'trace'
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
fastify.server.on('clientError', () => {
|
|
141
|
+
// this handler is called after default handler, so we can make sure end was not called
|
|
142
|
+
t.pass()
|
|
143
|
+
})
|
|
144
|
+
fastify.server.emit('clientError', new Error(), {
|
|
145
|
+
destroyed: true,
|
|
146
|
+
end () {
|
|
147
|
+
t.fail('end should not be called')
|
|
148
|
+
},
|
|
149
|
+
destroy () {
|
|
150
|
+
t.fail('destroy should not be called')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('default clientError handler destroys sockets in writable state', t => {
|
|
156
|
+
t.plan(1)
|
|
157
|
+
|
|
158
|
+
const fastify = Fastify({
|
|
159
|
+
bodyLimit: 1,
|
|
160
|
+
keepAliveTimeout: 100,
|
|
161
|
+
logger: {
|
|
162
|
+
level: 'trace'
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
fastify.server.emit('clientError', new Error(), {
|
|
167
|
+
destroyed: false,
|
|
168
|
+
writable: true,
|
|
169
|
+
encrypted: true,
|
|
170
|
+
end () {
|
|
171
|
+
t.fail('end should not be called')
|
|
172
|
+
},
|
|
173
|
+
destroy () {
|
|
174
|
+
t.pass('destroy should be called')
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('default clientError handler destroys http sockets in non-writable state', t => {
|
|
180
|
+
t.plan(1)
|
|
181
|
+
|
|
182
|
+
const fastify = Fastify({
|
|
183
|
+
bodyLimit: 1,
|
|
184
|
+
keepAliveTimeout: 100,
|
|
185
|
+
logger: {
|
|
186
|
+
level: 'trace'
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
fastify.server.emit('clientError', new Error(), {
|
|
191
|
+
destroyed: false,
|
|
192
|
+
writable: false,
|
|
193
|
+
end () {
|
|
194
|
+
t.fail('end should not be called')
|
|
195
|
+
},
|
|
196
|
+
destroy () {
|
|
197
|
+
t.pass('destroy should be called')
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
130
202
|
test('error handler binding', t => {
|
|
131
203
|
t.plan(5)
|
|
132
204
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
|
+
const Joi = require('@hapi/joi')
|
|
4
5
|
const AJV = require('ajv')
|
|
5
6
|
const S = require('fluent-json-schema')
|
|
6
7
|
const Fastify = require('..')
|
|
@@ -748,3 +749,34 @@ test('multiple refs with the same ids', t => {
|
|
|
748
749
|
t.same(res.json(), { hello: 'world' })
|
|
749
750
|
})
|
|
750
751
|
})
|
|
752
|
+
|
|
753
|
+
test('JOI validation overwrite request headers', t => {
|
|
754
|
+
t.plan(3)
|
|
755
|
+
const schemaValidator = ({ schema }) => data => {
|
|
756
|
+
const validationResult = schema.validate(data)
|
|
757
|
+
return validationResult
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
const fastify = Fastify()
|
|
761
|
+
fastify.setValidatorCompiler(schemaValidator)
|
|
762
|
+
|
|
763
|
+
fastify.get('/', {
|
|
764
|
+
schema: {
|
|
765
|
+
headers: Joi.object({
|
|
766
|
+
'user-agent': Joi.string().required(),
|
|
767
|
+
host: Joi.string().required()
|
|
768
|
+
})
|
|
769
|
+
}
|
|
770
|
+
}, (request, reply) => {
|
|
771
|
+
reply.send(request.headers)
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
fastify.inject('/', (err, res) => {
|
|
775
|
+
t.error(err)
|
|
776
|
+
t.equal(res.statusCode, 200)
|
|
777
|
+
t.same(res.json(), {
|
|
778
|
+
'user-agent': 'lightMyRequest',
|
|
779
|
+
host: 'localhost:80'
|
|
780
|
+
})
|
|
781
|
+
})
|
|
782
|
+
})
|