fastify 2.12.1 → 2.13.0
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/README.md +43 -26
- package/docs/Decorators.md +136 -75
- package/docs/Errors.md +5 -0
- package/docs/Reply.md +1 -1
- package/docs/Server.md +24 -0
- package/fastify.d.ts +5 -36
- package/fastify.js +20 -0
- package/lib/decorate.js +13 -2
- package/lib/errors.js +6 -0
- package/lib/route.js +18 -23
- package/package.json +21 -21
- package/test/decorator.test.js +32 -0
- package/test/internals/decorator.test.js +37 -2
- package/test/route.test.js +24 -24
- package/test/router-options.test.js +34 -0
- package/test/schemas.test.js +30 -0
- package/test/types/index.ts +10 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<div align="center">
|
|
17
17
|
|
|
18
18
|
[](https://www.npmjs.com/package/fastify)
|
|
19
|
-
[](https://www.npmjs.com/package/fastify)
|
|
19
|
+
[](https://www.npmjs.com/package/fastify)
|
|
20
20
|
[](https://github.com/nodejs/security-wg/blob/master/processes/responsible_disclosure_template.md)
|
|
22
22
|
|
|
@@ -29,8 +29,47 @@ How can you efficiently handle the resources of your server, knowing that you ar
|
|
|
29
29
|
|
|
30
30
|
Enter Fastify. Fastify is a web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.
|
|
31
31
|
|
|
32
|
+
### Quick start
|
|
33
|
+
|
|
34
|
+
Create a folder and make it your current working directory:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
mkdir my-app
|
|
38
|
+
cd my-app
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Generate a fastify project with `npm init`:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npm init fastify
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Install dependencies:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
npm install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
To start the app in dev mode:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npm run dev
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For production mode:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm start
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Under the hood `npm init` downloads and runs [Fastify Create](https://github.com/fastify/fastify-create),
|
|
66
|
+
which in turn uses the generate functionality of [Fastify CLI](https://github.com/fastify/fastify-cli).
|
|
67
|
+
|
|
68
|
+
|
|
32
69
|
### Install
|
|
33
70
|
|
|
71
|
+
If installing in an existing project, then Fastify can be installed into the project as a dependency:
|
|
72
|
+
|
|
34
73
|
Install with npm:
|
|
35
74
|
```
|
|
36
75
|
npm i fastify --save
|
|
@@ -80,29 +119,6 @@ fastify.listen(3000, (err, address) => {
|
|
|
80
119
|
|
|
81
120
|
Do you want to know more? Head to the <a href="https://github.com/fastify/fastify/blob/master/docs/Getting-Started.md"><code><b>Getting Started</b></code></a>.
|
|
82
121
|
|
|
83
|
-
### Quick start with Fastify CLI
|
|
84
|
-
|
|
85
|
-
Good tools make API development quicker and easier to maintain than doing everything manually.
|
|
86
|
-
|
|
87
|
-
The [Fastify CLI](https://github.com/fastify/fastify-cli) is a command line interface tool that can create new projects, manage plugins, and perform a variety of development tasks testing and running the application.
|
|
88
|
-
|
|
89
|
-
The goal in this guide is to build and run a simple Fastify project, using the [Fastify CLI](https://github.com/fastify/fastify-cli), while adhering to the Style Guide recommendations that benefit every Fastify project.
|
|
90
|
-
|
|
91
|
-
### Example
|
|
92
|
-
|
|
93
|
-
Open a terminal window.
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
npm install fastify-cli --global
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Generate a new project and default app by running the following command:
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
fastify generate
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
For more information, see the [Fastify CLI documentation](https://github.com/fastify/fastify-cli).
|
|
106
122
|
|
|
107
123
|
### Fastify v1.x
|
|
108
124
|
|
|
@@ -176,8 +192,8 @@ matters to you.
|
|
|
176
192
|
- [Live Examples](https://github.com/fastify/example) - Multirepo with a broad set of real working examples.
|
|
177
193
|
|
|
178
194
|
## Support
|
|
179
|
-
|
|
180
|
-
|
|
195
|
+
Please visit [Fastify help](https://github.com/fastify/help) to view prior
|
|
196
|
+
support issues and to ask new support questions.
|
|
181
197
|
|
|
182
198
|
## Team
|
|
183
199
|
|
|
@@ -191,6 +207,7 @@ Team members are listed in alphabetical order.
|
|
|
191
207
|
### Fastify Core team
|
|
192
208
|
* [__Tommaso Allevi__](https://github.com/allevo), <https://twitter.com/allevitommaso>, <https://www.npmjs.com/~allevo>
|
|
193
209
|
* [__Ethan Arrowood__](https://github.com/Ethan-Arrowood/), <https://twitter.com/arrowoodtech>, <https://www.npmjs.com/~ethan_arrowood>
|
|
210
|
+
* [__David Mark Clements__](https://github.com/davidmarkclements), <https://twitter.com/davidmarkclem>, <https://www.npmjs.com/~davidmarkclements>
|
|
194
211
|
* [__Matteo Collina__](https://github.com/mcollina), <https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
|
|
195
212
|
* [__Tomas Della Vedova__](https://github.com/delvedor), <https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
|
|
196
213
|
* [__Dustin Deus__](https://github.com/StarpTech), <https://twitter.com/dustindeus>, <https://www.npmjs.com/~starptech>
|
package/docs/Decorators.md
CHANGED
|
@@ -2,22 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
## Decorators
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The decorators API allows customization of the core Fastify objects, such as
|
|
6
|
+
the server instance itself and any request and reply objects used during the
|
|
7
|
+
HTTP request lifecycle. The decorators API can be used to attach any type of
|
|
8
|
+
property to the core objects, e.g. functions, plain objects, or native types.
|
|
9
|
+
|
|
10
|
+
This API is a *synchronous* API. Attempting to define a decoration
|
|
11
|
+
asynchronously could result in the Fastify instance booting prior to the
|
|
12
|
+
decoration completing its initialization. To avoid this issue, and register an
|
|
13
|
+
asynchronous 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
|
+
|
|
17
|
+
Decorating core objects with this API allows the underlying JavaScript engine
|
|
18
|
+
to optimize handling of the server, request, and reply objects. This is
|
|
19
|
+
accomplished by defining the shape of all such object instances before they are
|
|
20
|
+
instantiated and used. As an example, the following is not recommended because
|
|
21
|
+
it will change the shape of objects during their lifecycle:
|
|
6
22
|
|
|
7
|
-
|
|
23
|
+
```js
|
|
24
|
+
// Bad example! Continue reading.
|
|
25
|
+
|
|
26
|
+
// Attach a user property to the incoming request before the request
|
|
27
|
+
// handler is invoked.
|
|
28
|
+
fastify.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.
|
|
34
|
+
fastify.get('/', function (req, reply) {
|
|
35
|
+
reply.send(`Hello, ${req.user}`)
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Since the above example mutates the request object after it has already
|
|
40
|
+
been instantiated, the JavaScript engine must deoptimize access to the request
|
|
41
|
+
object. By using the decoration API this deoptimization is avoided:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
// Decorate request with a 'user' property
|
|
45
|
+
fastify.decorateRequest('user', '')
|
|
46
|
+
|
|
47
|
+
// Update our property
|
|
48
|
+
fastify.addHook('preHandler', (req, reply, done) => {
|
|
49
|
+
req.user = 'Bob Dylan'
|
|
50
|
+
done()
|
|
51
|
+
})
|
|
52
|
+
// And finally access it
|
|
53
|
+
fastify.get('/', (req, reply) => {
|
|
54
|
+
reply.send(`Hello, ${req.user}!`)
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See
|
|
59
|
+
[JavaScript engine fundamentals: Shapes and Inline Caches](https://web.archive.org/web/20200201163000/https://mathiasbynens.be/notes/shapes-ics)
|
|
60
|
+
for more information on this topic.
|
|
8
61
|
|
|
9
|
-
<a name="usage"></a>
|
|
10
62
|
### Usage
|
|
63
|
+
<a name="usage"></a>
|
|
64
|
+
|
|
65
|
+
#### `decorate(name, value, [dependencies])`
|
|
11
66
|
<a name="decorate"></a>
|
|
12
|
-
|
|
13
|
-
|
|
67
|
+
|
|
68
|
+
This method is used to customize the Fastify [server](Server.md) instance.
|
|
69
|
+
|
|
70
|
+
For example, to attach a new method to the server instance:
|
|
71
|
+
|
|
14
72
|
```js
|
|
15
|
-
fastify.decorate('utility', ()
|
|
73
|
+
fastify.decorate('utility', function () {
|
|
16
74
|
// Something very useful
|
|
17
75
|
})
|
|
18
76
|
```
|
|
19
77
|
|
|
20
|
-
As mentioned above,
|
|
78
|
+
As mentioned above, non-function values can be attached:
|
|
79
|
+
|
|
21
80
|
```js
|
|
22
81
|
fastify.decorate('conf', {
|
|
23
82
|
db: 'some.db',
|
|
@@ -25,39 +84,95 @@ fastify.decorate('conf', {
|
|
|
25
84
|
})
|
|
26
85
|
```
|
|
27
86
|
|
|
28
|
-
|
|
87
|
+
To access decorated properties, simply use the name provided to the
|
|
88
|
+
decoration API:
|
|
89
|
+
|
|
29
90
|
```js
|
|
30
91
|
fastify.utility()
|
|
31
92
|
|
|
32
93
|
console.log(fastify.conf.db)
|
|
33
94
|
```
|
|
34
95
|
|
|
96
|
+
The `dependencies` parameter is an optional list of decorators that the
|
|
97
|
+
decorator being defined relies upon. This list is simply a list of string names
|
|
98
|
+
of other decorators. In the following example, the "utility" decorator depends
|
|
99
|
+
upon "greet" and "log" decorators:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
fastify.decorate('utility', fn, ['greet', 'log'])
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If a dependency is not satisfied, the `decorate` method will throw an exception.
|
|
106
|
+
The dependency check is peformed before the server instance is booted. Thus,
|
|
107
|
+
it cannot occur during runtime.
|
|
108
|
+
|
|
109
|
+
#### `decorateReply(name, value, [dependencies])`
|
|
35
110
|
<a name="decorate-reply"></a>
|
|
36
|
-
|
|
37
|
-
As the name suggests, this API
|
|
111
|
+
|
|
112
|
+
As the name suggests, this API is used to add new methods/properties to the core
|
|
113
|
+
`Reply` object:
|
|
114
|
+
|
|
38
115
|
```js
|
|
39
116
|
fastify.decorateReply('utility', function () {
|
|
40
117
|
// Something very useful
|
|
41
118
|
})
|
|
42
119
|
```
|
|
43
120
|
|
|
44
|
-
Note: using an arrow function will break the binding of `this` to the Fastify
|
|
121
|
+
Note: using an arrow function will break the binding of `this` to the Fastify
|
|
122
|
+
`Reply` instance.
|
|
45
123
|
|
|
124
|
+
See [`decorate`](#decorate) for information about the `dependencies` parameter.
|
|
125
|
+
|
|
126
|
+
#### `decorateRequest(name, value, [dependencies])`
|
|
46
127
|
<a name="decorate-request"></a>
|
|
47
|
-
|
|
48
|
-
As above, this API is
|
|
128
|
+
|
|
129
|
+
As above with [`decorateReply`](#decorate-reply), this API is used add new
|
|
130
|
+
methods/properties to the core `Request` object:
|
|
131
|
+
|
|
49
132
|
```js
|
|
50
133
|
fastify.decorateRequest('utility', function () {
|
|
51
134
|
// something very useful
|
|
52
135
|
})
|
|
53
136
|
```
|
|
54
137
|
|
|
55
|
-
Note: using an arrow function will break the binding of `this` to the Fastify
|
|
138
|
+
Note: using an arrow function will break the binding of `this` to the Fastify
|
|
139
|
+
`Request` instance.
|
|
140
|
+
|
|
141
|
+
See [`decorate`](#decorate) for information about the `dependencies` parameter.
|
|
142
|
+
|
|
143
|
+
#### `hasDecorator(name)`
|
|
144
|
+
<a name="has-decorator"></a>
|
|
145
|
+
|
|
146
|
+
Used to check for the existence of a server instance decoration:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
fastify.hasDecorator('utility')
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### hasRequestDecorator
|
|
153
|
+
<a name="has-request-decorator"></a>
|
|
154
|
+
|
|
155
|
+
Used to check for the existence of a Request decoration:
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
fastify.hasRequestDecorator('utility')
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### hasReplyDecorator
|
|
162
|
+
<a name="has-reply-decorator"></a>
|
|
163
|
+
|
|
164
|
+
Used to check for the existence of a Reply decoration:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
fastify.hasReplyDecorator('utility')
|
|
168
|
+
```
|
|
56
169
|
|
|
170
|
+
### Decorators and Encapsulation
|
|
57
171
|
<a name="decorators-encapsulation"></a>
|
|
58
|
-
#### Decorators and Encapsulation
|
|
59
172
|
|
|
60
|
-
|
|
173
|
+
Defining a decorator (using `decorate`, `decorateRequest` or `decorateReply`)
|
|
174
|
+
with the same name more than once in the same **encapsulated** context will
|
|
175
|
+
throw an exception.
|
|
61
176
|
|
|
62
177
|
As an example, the following will throw:
|
|
63
178
|
|
|
@@ -107,10 +222,12 @@ server.register(async function (server, opts) {
|
|
|
107
222
|
server.listen(3000)
|
|
108
223
|
```
|
|
109
224
|
|
|
225
|
+
### Getters and Setters
|
|
110
226
|
<a name="getters-setters"></a>
|
|
111
|
-
#### Getters and Setters
|
|
112
227
|
|
|
113
|
-
Decorators accept special "getter/setter" objects. These objects have functions
|
|
228
|
+
Decorators accept special "getter/setter" objects. These objects have functions
|
|
229
|
+
named `getter` and `setter` (though, the `setter` function is optional). This
|
|
230
|
+
allows defining properties via decorators. For example:
|
|
114
231
|
|
|
115
232
|
```js
|
|
116
233
|
fastify.decorate('foo', {
|
|
@@ -120,64 +237,8 @@ fastify.decorate('foo', {
|
|
|
120
237
|
})
|
|
121
238
|
```
|
|
122
239
|
|
|
123
|
-
Will define the `foo` property on the
|
|
240
|
+
Will define the `foo` property on the Fastify instance:
|
|
124
241
|
|
|
125
242
|
```js
|
|
126
243
|
console.log(fastify.foo) // 'a getter'
|
|
127
244
|
```
|
|
128
|
-
|
|
129
|
-
<a name="usage_notes"></a>
|
|
130
|
-
#### Usage Notes
|
|
131
|
-
`decorateReply` and `decorateRequest` are used to modify the `Reply` and `Request` constructors respectively by adding methods or properties. To update these properties you should directly access the desired property of the `Reply` or `Request` object.
|
|
132
|
-
|
|
133
|
-
As an example let's add a user property to the `Request` object:
|
|
134
|
-
|
|
135
|
-
```js
|
|
136
|
-
// Decorate request with a 'user' property
|
|
137
|
-
fastify.decorateRequest('user', '')
|
|
138
|
-
|
|
139
|
-
// Update our property
|
|
140
|
-
fastify.addHook('preHandler', (req, reply, done) => {
|
|
141
|
-
req.user = 'Bob Dylan'
|
|
142
|
-
done()
|
|
143
|
-
})
|
|
144
|
-
// And finally access it
|
|
145
|
-
fastify.get('/', (req, reply) => {
|
|
146
|
-
reply.send(`Hello ${req.user}!`)
|
|
147
|
-
})
|
|
148
|
-
```
|
|
149
|
-
Note: The usage of `decorateReply` and `decorateRequest` is optional in this case but will allow Fastify to optimize for performance.
|
|
150
|
-
|
|
151
|
-
<a name="sync-async"></a>
|
|
152
|
-
#### Sync and Async
|
|
153
|
-
`decorate` is a *synchronous* API. If you need to add a decorator that has an *asynchronous* bootstrap, Fastify could boot up before your decorator is ready. To avoid this issue, you must use the `register` API in combination with `fastify-plugin`. To learn more, check out the [Plugins](https://github.com/fastify/fastify/blob/master/docs/Plugins.md) documentation as well.
|
|
154
|
-
|
|
155
|
-
<a name="dependencies"></a>
|
|
156
|
-
#### Dependencies
|
|
157
|
-
If your decorator depends on another decorator, you can easily declare the other decorator as a dependency. You just need to add an array of strings (representing the names of the decorators on which yours depends) as the third parameter:
|
|
158
|
-
```js
|
|
159
|
-
fastify.decorate('utility', fn, ['greet', 'log'])
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
If a dependency is not satisfied, `decorate` will throw an exception, but don't worry: the dependency check is executed before the server boots up, so it won't ever happen at runtime.
|
|
163
|
-
|
|
164
|
-
<a name="has-decorator"></a>
|
|
165
|
-
#### hasDecorator
|
|
166
|
-
You can check for the presence of a decorator with the `hasDecorator` API:
|
|
167
|
-
```js
|
|
168
|
-
fastify.hasDecorator('utility')
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
<a name="has-request-decorator"></a>
|
|
172
|
-
#### hasRequestDecorator
|
|
173
|
-
You can check for the presence of a Request decorator with the `hasRequestDecorator` API:
|
|
174
|
-
```js
|
|
175
|
-
fastify.hasRequestDecorator('utility')
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
<a name="has-reply-decorator"></a>
|
|
179
|
-
#### hasReplyDecorator
|
|
180
|
-
You can check for the presence of a Reply decorator with the `hasReplyDecorator` API:
|
|
181
|
-
```js
|
|
182
|
-
fastify.hasReplyDecorator('utility')
|
|
183
|
-
```
|
package/docs/Errors.md
CHANGED
|
@@ -16,6 +16,11 @@ If routes are declared as `async` though - the error will safely be caught by th
|
|
|
16
16
|
<a name="fastify-error-codes"></a>
|
|
17
17
|
### Fastify Error Codes
|
|
18
18
|
|
|
19
|
+
<a name="FST_ERR_BAD_URL"></a>
|
|
20
|
+
#### FST_ERR_BAD_URL
|
|
21
|
+
|
|
22
|
+
The router received an invalid url.
|
|
23
|
+
|
|
19
24
|
<a name="FST_ERR_CTP_ALREADY_PRESENT"></a>
|
|
20
25
|
#### FST_ERR_CTP_ALREADY_PRESENT
|
|
21
26
|
|
package/docs/Reply.md
CHANGED
|
@@ -338,7 +338,7 @@ fastify.get('/async-await', options, async function (request, reply) {
|
|
|
338
338
|
})
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
Rejected promises default to a `500` HTTP status code. Reject the promise, or `throw` in an `async function`, with an object that has `statusCode` (or `status`) and `message` properties to modify the reply.
|
|
341
|
+
Rejected promises default to a `500` HTTP status code. Reject the promise, or `throw` in an `async function`, with an _Error_ object that has `statusCode` (or `status`) and `message` properties to modify the reply. Throwing plain objects is not supported, it must be an instance of _Error_, see:
|
|
342
342
|
|
|
343
343
|
```js
|
|
344
344
|
fastify.get('/teapot', async function (request, reply) => {
|
package/docs/Server.md
CHANGED
|
@@ -418,6 +418,30 @@ Set a default
|
|
|
418
418
|
Note that this is needed to offer the graceful "close" experience when
|
|
419
419
|
using http2. Node core defaults this to `0`.
|
|
420
420
|
|
|
421
|
+
<a name="framework-errors"></a>
|
|
422
|
+
### `frameworkErrors`
|
|
423
|
+
|
|
424
|
+
+ Default: `null`
|
|
425
|
+
|
|
426
|
+
Fastify provides default error handlers for the most common use cases.
|
|
427
|
+
Using this option it is possible to override one or more of those handlers with custom code.
|
|
428
|
+
|
|
429
|
+
*Note: Only `FST_ERR_BAD_URL` is implemented at the moment.*
|
|
430
|
+
|
|
431
|
+
```js
|
|
432
|
+
const fastify = require('fastify')({
|
|
433
|
+
frameworkErrors: function (error, req, res) {
|
|
434
|
+
if (error instanceof FST_ERR_BAD_URL) {
|
|
435
|
+
res.code(400)
|
|
436
|
+
return res.send("Provided url is not valid")
|
|
437
|
+
} else {
|
|
438
|
+
res.send(err)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
})
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
|
|
421
445
|
## Instance
|
|
422
446
|
|
|
423
447
|
### Server Methods
|
package/fastify.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import * as ajv from 'ajv'
|
|
|
8
8
|
import * as http from 'http'
|
|
9
9
|
import * as http2 from 'http2'
|
|
10
10
|
import * as https from 'https'
|
|
11
|
+
import * as LightMyRequest from 'light-my-request'
|
|
11
12
|
|
|
12
13
|
declare function fastify<
|
|
13
14
|
HttpServer extends (http.Server | http2.Http2Server) = http.Server,
|
|
@@ -36,6 +37,10 @@ declare namespace fastify {
|
|
|
36
37
|
|
|
37
38
|
type HTTPMethod = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS'
|
|
38
39
|
|
|
40
|
+
// // Keep the original name of the interfaces to avoid braking change
|
|
41
|
+
interface HTTPInjectOptions extends LightMyRequest.InjectOptions {}
|
|
42
|
+
interface HTTPInjectResponse extends LightMyRequest.Response {}
|
|
43
|
+
|
|
39
44
|
interface ValidationResult {
|
|
40
45
|
keyword: string;
|
|
41
46
|
dataPath: string;
|
|
@@ -317,42 +322,6 @@ declare namespace fastify {
|
|
|
317
322
|
logSerializers?: Object
|
|
318
323
|
}
|
|
319
324
|
|
|
320
|
-
/**
|
|
321
|
-
* Fake http inject options
|
|
322
|
-
*/
|
|
323
|
-
interface HTTPInjectOptions {
|
|
324
|
-
url: string,
|
|
325
|
-
method?: HTTPMethod,
|
|
326
|
-
authority?: string,
|
|
327
|
-
headers?: DefaultHeaders,
|
|
328
|
-
query?: DefaultQuery,
|
|
329
|
-
remoteAddress?: string,
|
|
330
|
-
payload?: string | object | Buffer | NodeJS.ReadableStream
|
|
331
|
-
simulate?: {
|
|
332
|
-
end?: boolean,
|
|
333
|
-
split?: boolean,
|
|
334
|
-
error?: boolean,
|
|
335
|
-
close?: boolean
|
|
336
|
-
},
|
|
337
|
-
validate?: boolean
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Fake http inject response
|
|
342
|
-
*/
|
|
343
|
-
interface HTTPInjectResponse {
|
|
344
|
-
raw: {
|
|
345
|
-
req: NodeJS.ReadableStream,
|
|
346
|
-
res: http.ServerResponse
|
|
347
|
-
},
|
|
348
|
-
headers: Record<string, string>,
|
|
349
|
-
statusCode: number,
|
|
350
|
-
statusMessage: string,
|
|
351
|
-
payload: string,
|
|
352
|
-
rawPayload: Buffer,
|
|
353
|
-
trailers: object
|
|
354
|
-
}
|
|
355
|
-
|
|
356
325
|
/**
|
|
357
326
|
* Server listen options
|
|
358
327
|
*/
|
package/fastify.js
CHANGED
|
@@ -42,6 +42,11 @@ const { buildRouting, validateBodyLimitOption } = require('./lib/route')
|
|
|
42
42
|
const build404 = require('./lib/fourOhFour')
|
|
43
43
|
const getSecuredInitialConfig = require('./lib/initialConfigValidation')
|
|
44
44
|
const { defaultInitOptions } = getSecuredInitialConfig
|
|
45
|
+
const {
|
|
46
|
+
codes: {
|
|
47
|
+
FST_ERR_BAD_URL
|
|
48
|
+
}
|
|
49
|
+
} = require('./lib/errors')
|
|
45
50
|
|
|
46
51
|
function build (options) {
|
|
47
52
|
// Options validations
|
|
@@ -73,6 +78,7 @@ function build (options) {
|
|
|
73
78
|
customOptions: {},
|
|
74
79
|
plugins: []
|
|
75
80
|
}, options.ajv)
|
|
81
|
+
const frameworkErrors = options.frameworkErrors
|
|
76
82
|
|
|
77
83
|
// Ajv options
|
|
78
84
|
if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
|
|
@@ -431,6 +437,20 @@ function build (options) {
|
|
|
431
437
|
}
|
|
432
438
|
|
|
433
439
|
function onBadUrl (path, req, res) {
|
|
440
|
+
if (frameworkErrors) {
|
|
441
|
+
req.id = genReqId(req)
|
|
442
|
+
req.originalUrl = req.url
|
|
443
|
+
var childLogger = logger.child({ reqId: req.id })
|
|
444
|
+
if (modifyCoreObjects) {
|
|
445
|
+
req.log = res.log = childLogger
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
childLogger.info({ req }, 'incoming request')
|
|
449
|
+
|
|
450
|
+
const request = new Request(null, req, null, req.headers, childLogger)
|
|
451
|
+
const reply = new Reply(res, { onSend: [], onError: [] }, request, childLogger)
|
|
452
|
+
return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
|
|
453
|
+
}
|
|
434
454
|
const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
|
|
435
455
|
res.writeHead(400, {
|
|
436
456
|
'Content-Type': 'application/json',
|
package/lib/decorate.js
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
kReply,
|
|
7
|
-
kRequest
|
|
7
|
+
kRequest,
|
|
8
|
+
kState
|
|
8
9
|
} = require('./symbols.js')
|
|
9
10
|
|
|
10
11
|
const {
|
|
11
12
|
codes: {
|
|
12
13
|
FST_ERR_DEC_ALREADY_PRESENT,
|
|
13
|
-
FST_ERR_DEC_MISSING_DEPENDENCY
|
|
14
|
+
FST_ERR_DEC_MISSING_DEPENDENCY,
|
|
15
|
+
FST_ERR_DEC_AFTER_START
|
|
14
16
|
}
|
|
15
17
|
} = require('./errors')
|
|
16
18
|
|
|
@@ -34,6 +36,7 @@ function decorate (instance, name, fn, dependencies) {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
function decorateFastify (name, fn, dependencies) {
|
|
39
|
+
assertNotStarted(this, name)
|
|
37
40
|
decorate(this, name, fn, dependencies)
|
|
38
41
|
return this
|
|
39
42
|
}
|
|
@@ -63,15 +66,23 @@ function checkDependencies (instance, deps) {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function decorateReply (name, fn, dependencies) {
|
|
69
|
+
assertNotStarted(this, name)
|
|
66
70
|
decorate(this[kReply].prototype, name, fn, dependencies)
|
|
67
71
|
return this
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
function decorateRequest (name, fn, dependencies) {
|
|
75
|
+
assertNotStarted(this, name)
|
|
71
76
|
decorate(this[kRequest].prototype, name, fn, dependencies)
|
|
72
77
|
return this
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
function assertNotStarted (instance, name) {
|
|
81
|
+
if (instance[kState].started) {
|
|
82
|
+
throw new FST_ERR_DEC_AFTER_START(name)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
module.exports = {
|
|
76
87
|
add: decorateFastify,
|
|
77
88
|
exist: checkExistence,
|
package/lib/errors.js
CHANGED
|
@@ -27,6 +27,7 @@ createError('FST_ERR_CTP_EMPTY_JSON_BODY', "Body cannot be empty when content-ty
|
|
|
27
27
|
*/
|
|
28
28
|
createError('FST_ERR_DEC_ALREADY_PRESENT', "The decorator '%s' has already been added!")
|
|
29
29
|
createError('FST_ERR_DEC_MISSING_DEPENDENCY', "The decorator is missing dependency '%s'.")
|
|
30
|
+
createError('FST_ERR_DEC_AFTER_START', "The decorator '%s' has been added after start!")
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* hooks
|
|
@@ -74,6 +75,11 @@ createError('FST_ERR_HTTP2_INVALID_VERSION', 'HTTP2 is available only from node
|
|
|
74
75
|
*/
|
|
75
76
|
createError('FST_ERR_INIT_OPTS_INVALID', "Invalid initialization options: '%s'")
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* router
|
|
80
|
+
*/
|
|
81
|
+
createError('FST_ERR_BAD_URL', "'%s' is not a valid url component", 400)
|
|
82
|
+
|
|
77
83
|
function createError (code, message, statusCode = 500, Base = Error) {
|
|
78
84
|
if (!code) throw new Error('Fastify error code must not be empty')
|
|
79
85
|
if (!message) throw new Error('Fastify error message must not be empty')
|
package/lib/route.js
CHANGED
|
@@ -218,29 +218,6 @@ function buildRouting (options) {
|
|
|
218
218
|
this[kReplySerializerDefault]
|
|
219
219
|
)
|
|
220
220
|
|
|
221
|
-
// TODO this needs to be refactored so that buildSchemaCompiler is
|
|
222
|
-
// not called for every single route. Creating a new one for every route
|
|
223
|
-
// is going to be very expensive.
|
|
224
|
-
if (opts.schema) {
|
|
225
|
-
if (this[kSchemaCompiler] == null && this[kSchemaResolver]) {
|
|
226
|
-
done(new FST_ERR_SCH_MISSING_COMPILER(opts.method, url))
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
|
|
232
|
-
const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
|
|
233
|
-
this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
|
|
237
|
-
} catch (error) {
|
|
238
|
-
// bubble up the FastifyError instance
|
|
239
|
-
done(error.code ? error : new FST_ERR_SCH_BUILD(opts.method, url, error.message))
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
221
|
for (const hook of supportedHooks) {
|
|
245
222
|
if (opts[hook]) {
|
|
246
223
|
if (Array.isArray(opts[hook])) {
|
|
@@ -280,6 +257,24 @@ function buildRouting (options) {
|
|
|
280
257
|
// Must store the 404 Context in 'preReady' because it is only guaranteed to
|
|
281
258
|
// be available after all of the plugins and routes have been loaded.
|
|
282
259
|
fourOhFour.setContext(this, context)
|
|
260
|
+
|
|
261
|
+
if (opts.schema) {
|
|
262
|
+
if (this[kSchemaCompiler] == null && this[kSchemaResolver]) {
|
|
263
|
+
throw new FST_ERR_SCH_MISSING_COMPILER(opts.method, url)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
|
|
268
|
+
const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
|
|
269
|
+
this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
|
|
273
|
+
} catch (error) {
|
|
274
|
+
// bubble up the FastifyError instance
|
|
275
|
+
throw (error.code ? error : new FST_ERR_SCH_BUILD(opts.method, url, error.message))
|
|
276
|
+
}
|
|
277
|
+
}
|
|
283
278
|
})
|
|
284
279
|
|
|
285
280
|
done(notHandledErr)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"typings": "fastify.d.ts",
|
|
@@ -92,28 +92,28 @@
|
|
|
92
92
|
"node": ">=6"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@types/node": "^12.12.
|
|
96
|
-
"@typescript-eslint/eslint-plugin": "^2.
|
|
97
|
-
"@typescript-eslint/parser": "^2.
|
|
95
|
+
"@types/node": "^12.12.30",
|
|
96
|
+
"@typescript-eslint/eslint-plugin": "^2.24.0",
|
|
97
|
+
"@typescript-eslint/parser": "^2.24.0",
|
|
98
98
|
"JSONStream": "^1.3.5",
|
|
99
99
|
"ajv-merge-patch": "^4.1.0",
|
|
100
100
|
"ajv-pack": "^0.3.1",
|
|
101
101
|
"autocannon": "^3.2.2",
|
|
102
102
|
"branch-comparer": "^0.4.0",
|
|
103
|
-
"concurrently": "^5.0
|
|
103
|
+
"concurrently": "^5.1.0",
|
|
104
104
|
"cors": "^2.8.5",
|
|
105
|
-
"coveralls": "^3.0.
|
|
105
|
+
"coveralls": "^3.0.11",
|
|
106
106
|
"dns-prefetch-control": "^0.2.0",
|
|
107
107
|
"eslint": "^6.7.2",
|
|
108
|
-
"eslint-import-resolver-node": "^0.3.
|
|
108
|
+
"eslint-import-resolver-node": "^0.3.3",
|
|
109
109
|
"events.once": "^2.0.2",
|
|
110
110
|
"fast-json-body": "^1.1.0",
|
|
111
|
-
"fastify-plugin": "^1.
|
|
111
|
+
"fastify-plugin": "^1.6.1",
|
|
112
112
|
"fluent-schema": "^0.10.0",
|
|
113
113
|
"form-data": "^3.0.0",
|
|
114
114
|
"frameguard": "^3.0.0",
|
|
115
115
|
"h2url": "^0.2.0",
|
|
116
|
-
"helmet": "^3.
|
|
116
|
+
"helmet": "^3.21.3",
|
|
117
117
|
"hide-powered-by": "^1.0.0",
|
|
118
118
|
"hsts": "^2.1.0",
|
|
119
119
|
"http-errors": "^1.7.1",
|
|
@@ -130,28 +130,28 @@
|
|
|
130
130
|
"simple-get": "^3.0.3",
|
|
131
131
|
"snazzy": "^8.0.0",
|
|
132
132
|
"split2": "^3.1.0",
|
|
133
|
-
"standard": "^14.
|
|
133
|
+
"standard": "^14.3.3",
|
|
134
134
|
"tap": "^12.5.2",
|
|
135
135
|
"tap-mocha-reporter": "^3.0.7",
|
|
136
136
|
"then-sleep": "^1.0.1",
|
|
137
|
-
"typescript": "^3.
|
|
137
|
+
"typescript": "^3.8.3",
|
|
138
138
|
"x-xss-protection": "^1.1.0",
|
|
139
|
-
"yup": "^0.28.
|
|
139
|
+
"yup": "^0.28.3"
|
|
140
140
|
},
|
|
141
141
|
"dependencies": {
|
|
142
142
|
"abstract-logging": "^2.0.0",
|
|
143
|
-
"ajv": "^6.
|
|
144
|
-
"avvio": "^6.3.
|
|
145
|
-
"fast-json-stringify": "^1.
|
|
146
|
-
"find-my-way": "^2.2.
|
|
143
|
+
"ajv": "^6.12.0",
|
|
144
|
+
"avvio": "^6.3.1",
|
|
145
|
+
"fast-json-stringify": "^1.18.0",
|
|
146
|
+
"find-my-way": "^2.2.2",
|
|
147
147
|
"flatstr": "^1.0.12",
|
|
148
|
-
"light-my-request": "^3.7.
|
|
148
|
+
"light-my-request": "^3.7.2",
|
|
149
149
|
"middie": "^4.1.0",
|
|
150
|
-
"pino": "^5.
|
|
151
|
-
"proxy-addr": "^2.0.
|
|
152
|
-
"readable-stream": "^3.
|
|
150
|
+
"pino": "^5.17.0",
|
|
151
|
+
"proxy-addr": "^2.0.6",
|
|
152
|
+
"readable-stream": "^3.6.0",
|
|
153
153
|
"rfdc": "^1.1.2",
|
|
154
|
-
"secure-json-parse": "^2.
|
|
154
|
+
"secure-json-parse": "^2.1.0",
|
|
155
155
|
"tiny-lru": "^7.0.2"
|
|
156
156
|
},
|
|
157
157
|
"greenkeeper": {
|
package/test/decorator.test.js
CHANGED
|
@@ -708,3 +708,35 @@ test('after can access to a decorated instance and previous plugin decoration',
|
|
|
708
708
|
t.equal(response.statusCode, 200)
|
|
709
709
|
})
|
|
710
710
|
})
|
|
711
|
+
|
|
712
|
+
test('decorate* should throw if called after ready', t => {
|
|
713
|
+
t.plan(3)
|
|
714
|
+
const fastify = Fastify()
|
|
715
|
+
|
|
716
|
+
fastify.get('/', (request, reply) => {
|
|
717
|
+
reply.send({
|
|
718
|
+
hello: 'world'
|
|
719
|
+
})
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
fastify.listen(0)
|
|
723
|
+
.then(() => {
|
|
724
|
+
try {
|
|
725
|
+
fastify.decorate('test', true)
|
|
726
|
+
} catch (e) {
|
|
727
|
+
t.is(e.message, "FST_ERR_DEC_AFTER_START: The decorator 'test' has been added after start!")
|
|
728
|
+
}
|
|
729
|
+
try {
|
|
730
|
+
fastify.decorateRequest('test', true)
|
|
731
|
+
} catch (e) {
|
|
732
|
+
t.is(e.message, "FST_ERR_DEC_AFTER_START: The decorator 'test' has been added after start!")
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
fastify.decorateReply('test', true)
|
|
736
|
+
} catch (e) {
|
|
737
|
+
t.is(e.message, "FST_ERR_DEC_AFTER_START: The decorator 'test' has been added after start!")
|
|
738
|
+
}
|
|
739
|
+
return fastify.close()
|
|
740
|
+
})
|
|
741
|
+
.catch(err => t.fail(err))
|
|
742
|
+
})
|
|
@@ -5,11 +5,19 @@
|
|
|
5
5
|
const t = require('tap')
|
|
6
6
|
const test = t.test
|
|
7
7
|
const decorator = require('../../lib/decorate')
|
|
8
|
+
const {
|
|
9
|
+
kState
|
|
10
|
+
} = require('../../lib/symbols')
|
|
8
11
|
|
|
9
12
|
test('decorate should add the given method to its instance', t => {
|
|
10
13
|
t.plan(1)
|
|
11
14
|
function build () {
|
|
12
15
|
server.add = decorator.add
|
|
16
|
+
server[kState] = {
|
|
17
|
+
listening: false,
|
|
18
|
+
closing: false,
|
|
19
|
+
started: false
|
|
20
|
+
}
|
|
13
21
|
return server
|
|
14
22
|
function server () {}
|
|
15
23
|
}
|
|
@@ -23,6 +31,11 @@ test('decorate is chainable', t => {
|
|
|
23
31
|
t.plan(3)
|
|
24
32
|
function build () {
|
|
25
33
|
server.add = decorator.add
|
|
34
|
+
server[kState] = {
|
|
35
|
+
listening: false,
|
|
36
|
+
closing: false,
|
|
37
|
+
started: false
|
|
38
|
+
}
|
|
26
39
|
return server
|
|
27
40
|
function server () {}
|
|
28
41
|
}
|
|
@@ -49,6 +62,11 @@ test('checkExistence should find the instance if not given', t => {
|
|
|
49
62
|
function build () {
|
|
50
63
|
server.add = decorator.add
|
|
51
64
|
server.check = decorator.exist
|
|
65
|
+
server[kState] = {
|
|
66
|
+
listening: false,
|
|
67
|
+
closing: false,
|
|
68
|
+
started: false
|
|
69
|
+
}
|
|
52
70
|
return server
|
|
53
71
|
function server () {}
|
|
54
72
|
}
|
|
@@ -83,6 +101,11 @@ test('decorate should internally call checkDependencies', t => {
|
|
|
83
101
|
t.plan(2)
|
|
84
102
|
function build () {
|
|
85
103
|
server.add = decorator.add
|
|
104
|
+
server[kState] = {
|
|
105
|
+
listening: false,
|
|
106
|
+
closing: false,
|
|
107
|
+
started: false
|
|
108
|
+
}
|
|
86
109
|
return server
|
|
87
110
|
function server () {}
|
|
88
111
|
}
|
|
@@ -101,7 +124,13 @@ test('decorate should internally call checkDependencies', t => {
|
|
|
101
124
|
test('decorate should recognize getter/setter objects', t => {
|
|
102
125
|
t.plan(6)
|
|
103
126
|
|
|
104
|
-
const one = {
|
|
127
|
+
const one = {
|
|
128
|
+
[kState]: {
|
|
129
|
+
listening: false,
|
|
130
|
+
closing: false,
|
|
131
|
+
started: false
|
|
132
|
+
}
|
|
133
|
+
}
|
|
105
134
|
decorator.add.call(one, 'foo', {
|
|
106
135
|
getter: () => this._a,
|
|
107
136
|
setter: (val) => {
|
|
@@ -115,7 +144,13 @@ test('decorate should recognize getter/setter objects', t => {
|
|
|
115
144
|
t.is(one.foo, 'a')
|
|
116
145
|
|
|
117
146
|
// getter only
|
|
118
|
-
const two = {
|
|
147
|
+
const two = {
|
|
148
|
+
[kState]: {
|
|
149
|
+
listening: false,
|
|
150
|
+
closing: false,
|
|
151
|
+
started: false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
119
154
|
decorator.add.call(two, 'foo', {
|
|
120
155
|
getter: () => 'a getter'
|
|
121
156
|
})
|
package/test/route.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const joi = require('joi')
|
|
|
7
7
|
const Fastify = require('..')
|
|
8
8
|
|
|
9
9
|
test('route', t => {
|
|
10
|
-
t.plan(
|
|
10
|
+
t.plan(9)
|
|
11
11
|
const test = t.test
|
|
12
12
|
const fastify = Fastify()
|
|
13
13
|
|
|
@@ -65,29 +65,6 @@ test('route', t => {
|
|
|
65
65
|
}
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
test('invalid schema - route', t => {
|
|
69
|
-
t.plan(1)
|
|
70
|
-
try {
|
|
71
|
-
fastify.route({
|
|
72
|
-
method: 'GET',
|
|
73
|
-
url: '/invalid',
|
|
74
|
-
schema: {
|
|
75
|
-
querystring: {
|
|
76
|
-
id: 'string'
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
handler: function (req, reply) {
|
|
80
|
-
reply.send({ hello: 'world' })
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
fastify.after(err => {
|
|
84
|
-
t.ok(err instanceof Error)
|
|
85
|
-
})
|
|
86
|
-
} catch (e) {
|
|
87
|
-
t.fail()
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
68
|
test('Multiple methods', t => {
|
|
92
69
|
t.plan(1)
|
|
93
70
|
try {
|
|
@@ -190,6 +167,29 @@ test('route', t => {
|
|
|
190
167
|
})
|
|
191
168
|
})
|
|
192
169
|
|
|
170
|
+
test('invalid schema - route', t => {
|
|
171
|
+
t.plan(3)
|
|
172
|
+
|
|
173
|
+
const fastify = Fastify()
|
|
174
|
+
fastify.route({
|
|
175
|
+
handler: () => {},
|
|
176
|
+
method: 'GET',
|
|
177
|
+
url: '/invalid',
|
|
178
|
+
schema: {
|
|
179
|
+
querystring: {
|
|
180
|
+
id: 'string'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
fastify.after(err => {
|
|
185
|
+
t.notOk(err, 'the error is throw on preReady')
|
|
186
|
+
})
|
|
187
|
+
fastify.ready(err => {
|
|
188
|
+
t.is(err.code, 'FST_ERR_SCH_BUILD')
|
|
189
|
+
t.isLike(err.message, /Failed building the schema for GET: \/invalid/)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
193
|
test('path can be specified in place of uri', t => {
|
|
194
194
|
t.plan(3)
|
|
195
195
|
const fastify = Fastify()
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
const test = require('tap').test
|
|
4
4
|
const sget = require('simple-get')
|
|
5
5
|
const Fastify = require('../')
|
|
6
|
+
const {
|
|
7
|
+
codes: {
|
|
8
|
+
FST_ERR_BAD_URL
|
|
9
|
+
}
|
|
10
|
+
} = require('../lib/errors')
|
|
6
11
|
|
|
7
12
|
test('Should honor ignoreTrailingSlash option', t => {
|
|
8
13
|
t.plan(4)
|
|
@@ -58,3 +63,32 @@ test('Should honor maxParamLength option', t => {
|
|
|
58
63
|
t.strictEqual(res.statusCode, 404)
|
|
59
64
|
})
|
|
60
65
|
})
|
|
66
|
+
|
|
67
|
+
test('Should honor frameworkErrors option', t => {
|
|
68
|
+
t.plan(3)
|
|
69
|
+
const fastify = Fastify({
|
|
70
|
+
frameworkErrors: function (err, req, res) {
|
|
71
|
+
if (err instanceof FST_ERR_BAD_URL) {
|
|
72
|
+
t.ok(true)
|
|
73
|
+
} else {
|
|
74
|
+
t.fail()
|
|
75
|
+
}
|
|
76
|
+
res.send(err.message)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
fastify.get('/test/:id', (req, res) => {
|
|
81
|
+
res.send('{ hello: \'world\' }')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
fastify.inject(
|
|
85
|
+
{
|
|
86
|
+
method: 'GET',
|
|
87
|
+
url: '/test/%world'
|
|
88
|
+
},
|
|
89
|
+
(err, res) => {
|
|
90
|
+
t.error(err)
|
|
91
|
+
t.equals(res.body, 'FST_ERR_BAD_URL: \'%world\' is not a valid url component')
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
})
|
package/test/schemas.test.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const t = require('tap')
|
|
4
4
|
const test = t.test
|
|
5
5
|
const Fastify = require('..')
|
|
6
|
+
const fp = require('fastify-plugin')
|
|
6
7
|
|
|
7
8
|
const ajvMergePatch = require('ajv-merge-patch')
|
|
8
9
|
const AJV = require('ajv')
|
|
@@ -448,3 +449,32 @@ test('Should handle root $patch keywords in header', t => {
|
|
|
448
449
|
})
|
|
449
450
|
})
|
|
450
451
|
})
|
|
452
|
+
|
|
453
|
+
test('Add schema order should not break the startup', t => {
|
|
454
|
+
t.plan(1)
|
|
455
|
+
const fastify = Fastify()
|
|
456
|
+
|
|
457
|
+
fastify.get('/', { schema: { random: 'options' } }, () => {})
|
|
458
|
+
|
|
459
|
+
fastify.register(fp((f, opts) => {
|
|
460
|
+
f.addSchema({
|
|
461
|
+
$id: 'https://example.com/bson/objectId',
|
|
462
|
+
type: 'string',
|
|
463
|
+
pattern: '\\b[0-9A-Fa-f]{24}\\b'
|
|
464
|
+
})
|
|
465
|
+
return Promise.resolve() // avoid async for node 6
|
|
466
|
+
}))
|
|
467
|
+
|
|
468
|
+
fastify.get('/:id', {
|
|
469
|
+
schema: {
|
|
470
|
+
params: {
|
|
471
|
+
type: 'object',
|
|
472
|
+
properties: {
|
|
473
|
+
id: { $ref: 'https://example.com/bson/objectId#' }
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}, () => {})
|
|
478
|
+
|
|
479
|
+
fastify.ready(err => { t.error(err) })
|
|
480
|
+
})
|
package/test/types/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import * as fastify from '../../fastify'
|
|
|
6
6
|
import * as http from 'http'
|
|
7
7
|
import * as http2 from 'http2'
|
|
8
8
|
import { readFileSync } from 'fs'
|
|
9
|
+
import * as LightMyRequest from 'light-my-request'
|
|
9
10
|
|
|
10
11
|
// were importing cors using require, which causes it to be an `any`. This is done because `cors` exports
|
|
11
12
|
// itself as an express.RequestHandler which is not compatible with the fastify TypeScript types
|
|
@@ -609,11 +610,20 @@ server.listen({
|
|
|
609
610
|
}).then((address: string) => console.log(address))
|
|
610
611
|
|
|
611
612
|
// http injections
|
|
613
|
+
server.inject({ url: '/test' }, (err: Error, res: LightMyRequest.Response) => {
|
|
614
|
+
server.log.debug(err)
|
|
615
|
+
server.log.debug(res.payload)
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
// http injections with the fastify types
|
|
612
619
|
server.inject({ url: '/test' }, (err: Error, res: fastify.HTTPInjectResponse) => {
|
|
613
620
|
server.log.debug(err)
|
|
614
621
|
server.log.debug(res.payload)
|
|
615
622
|
})
|
|
616
623
|
|
|
624
|
+
server.inject({ url: '/testAgain' })
|
|
625
|
+
.then((res: LightMyRequest.Response) => console.log(res.payload))
|
|
626
|
+
|
|
617
627
|
server.inject({ url: '/testAgain' })
|
|
618
628
|
.then((res: fastify.HTTPInjectResponse) => console.log(res.payload))
|
|
619
629
|
|