fastify 3.5.0 → 3.8.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/docs/ContentTypeParser.md +3 -0
- package/docs/Ecosystem.md +7 -2
- package/docs/Hooks.md +0 -5
- package/docs/Lifecycle.md +2 -2
- package/docs/Logging.md +1 -1
- package/docs/Recommendations.md +17 -0
- package/docs/Reply.md +2 -2
- package/docs/Request.md +4 -2
- package/docs/Routes.md +21 -3
- package/docs/Server.md +51 -3
- package/docs/Style-Guide.md +180 -0
- package/docs/TypeScript.md +359 -359
- package/docs/Validation-and-Serialization.md +11 -5
- package/examples/parser.js +1 -1
- package/fastify.d.ts +2 -2
- package/fastify.js +39 -7
- package/lib/contentTypeParser.js +16 -11
- package/lib/context.js +5 -19
- package/lib/decorate.js +1 -1
- package/lib/errors.js +2 -2
- package/lib/fourOhFour.js +8 -7
- package/lib/handleRequest.js +5 -5
- package/lib/hooks.js +4 -4
- package/lib/logger.js +7 -7
- package/lib/pluginUtils.js +4 -2
- package/lib/reply.js +27 -24
- package/lib/reqIdGenFactory.js +2 -2
- package/lib/request.js +20 -8
- package/lib/route.js +12 -10
- package/lib/schemas.js +1 -1
- package/lib/server.js +5 -5
- package/lib/symbols.js +2 -1
- package/lib/validation.js +9 -8
- package/lib/warnings.js +3 -0
- package/lib/wrapThenable.js +2 -2
- package/package.json +10 -10
- package/test/404s.test.js +15 -15
- package/test/async-await.test.js +7 -7
- package/test/custom-parser-async.test.js +2 -2
- package/test/custom-parser.test.js +38 -8
- package/test/emit-warning.test.js +31 -0
- package/test/fastify-instance.test.js +33 -1
- package/test/helper.js +1 -1
- package/test/hooks.test.js +6 -6
- package/test/http2/head.test.js +1 -1
- package/test/http2/plain.test.js +1 -1
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/http2/secure.test.js +1 -1
- package/test/http2/unknown-http-method.test.js +1 -1
- package/test/https/https.test.js +2 -1
- package/test/inject.test.js +2 -2
- package/test/internals/all.test.js +1 -1
- package/test/internals/hookRunner.test.js +1 -1
- package/test/internals/logger.test.js +1 -1
- package/test/internals/reply.test.js +62 -7
- package/test/internals/request.test.js +31 -5
- package/test/internals/version.test.js +43 -0
- package/test/listen.test.js +12 -0
- package/test/logger.test.js +11 -11
- package/test/nullable-validation.test.js +108 -0
- package/test/pretty-print.test.js +9 -14
- package/test/reply-error.test.js +2 -2
- package/test/request-error.test.js +81 -0
- package/test/route-hooks.test.js +10 -10
- package/test/stream.test.js +11 -11
- package/test/types/fastify.test-d.ts +1 -2
- package/test/types/instance.test-d.ts +17 -3
- package/test/types/logger.test-d.ts +35 -2
- package/test/types/register.test-d.ts +16 -0
- package/test/types/request.test-d.ts +1 -1
- package/test/types/route.test-d.ts +5 -0
- package/test/validation-error-handling.test.js +66 -0
- package/test/versioned-routes.test.js +55 -0
- package/types/.eslintrc.json +4 -1
- package/types/content-type-parser.d.ts +0 -15
- package/types/hooks.d.ts +138 -16
- package/types/instance.d.ts +86 -14
- package/types/logger.d.ts +13 -11
- package/types/plugin.d.ts +5 -7
- package/types/register.d.ts +8 -7
- package/types/request.d.ts +3 -1
- package/types/route.d.ts +38 -58
- package/types/schema.d.ts +4 -4
- package/types/serverFactory.d.ts +9 -9
package/docs/TypeScript.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
The Fastify framework is written in vanilla JavaScript, and as such type definitions are not as easy to maintain; however, since version 2 and beyond, maintainers and contributors have put in a great effort to improve the types.
|
|
6
6
|
|
|
7
|
-
The type system was changed in Fastify version 3. The new type system introduces generic constraining and defaulting, plus a new way to define schema types such as a request body, querystring, and more! As the team works on improving framework and type definition synergy, sometimes parts of the API will not be typed or may be typed incorrectly. We encourage you to **contribute** to help us fill in the gaps. Just make sure to read our [`CONTRIBUTING.md`](../CONTRIBUTING.md) file before getting started to make sure things go smoothly!
|
|
7
|
+
The type system was changed in Fastify version 3. The new type system introduces generic constraining and defaulting, plus a new way to define schema types such as a request body, querystring, and more! As the team works on improving framework and type definition synergy, sometimes parts of the API will not be typed or may be typed incorrectly. We encourage you to **contribute** to help us fill in the gaps. Just make sure to read our [`CONTRIBUTING.md`](../CONTRIBUTING.md) file before getting started to make sure things go smoothly!
|
|
8
8
|
|
|
9
9
|
> The documentation in this section covers Fastify version 3.x typings
|
|
10
10
|
|
|
@@ -14,11 +14,11 @@ The type system was changed in Fastify version 3. The new type system introduces
|
|
|
14
14
|
|
|
15
15
|
## Learn By Example
|
|
16
16
|
|
|
17
|
-
The best way to learn the Fastify type system is by example! The following four examples should cover the most common Fastify development cases. After the examples there is further, more detailed documentation for the type system.
|
|
17
|
+
The best way to learn the Fastify type system is by example! The following four examples should cover the most common Fastify development cases. After the examples there is further, more detailed documentation for the type system.
|
|
18
18
|
|
|
19
19
|
### Getting Started
|
|
20
20
|
|
|
21
|
-
This example will get you up and running with Fastify and TypeScript. It results in a blank http Fastify server.
|
|
21
|
+
This example will get you up and running with Fastify and TypeScript. It results in a blank http Fastify server.
|
|
22
22
|
|
|
23
23
|
1. Create a new npm project, install Fastify, and install typescript & node.js types as peer dependencies:
|
|
24
24
|
```bash
|
|
@@ -39,27 +39,27 @@ This example will get you up and running with Fastify and TypeScript. It results
|
|
|
39
39
|
```bash
|
|
40
40
|
npx typescript --init
|
|
41
41
|
```
|
|
42
|
-
or use one of the [recommended ones](https://github.com/tsconfig/bases#node-10-tsconfigjson).
|
|
42
|
+
or use one of the [recommended ones](https://github.com/tsconfig/bases#node-10-tsconfigjson).
|
|
43
43
|
|
|
44
44
|
4. Create an `index.ts` file - this will contain the server code
|
|
45
45
|
5. Add the following code block to your file:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
```typescript
|
|
47
|
+
import fastify from 'fastify'
|
|
48
|
+
|
|
49
|
+
const server = fastify()
|
|
50
|
+
|
|
51
|
+
server.get('/ping', async (request, reply) => {
|
|
52
|
+
return 'pong\n'
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
server.listen(8080, (err, address) => {
|
|
56
|
+
if (err) {
|
|
57
|
+
console.error(err)
|
|
58
|
+
process.exit(1)
|
|
59
|
+
}
|
|
60
|
+
console.log(`Server listening at ${address}`)
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
63
|
6. Run `npm run build` - this will compile `index.ts` into `index.js` which can be executed using Node.js. If you run into any errors please open an issue in [fastify/help](https://github.com/fastify/help/)
|
|
64
64
|
7. Run `npm run start` to run the Fastify server
|
|
65
65
|
8. You should see `Server listening at http://127.0.0.1:8080` in your console
|
|
@@ -75,51 +75,51 @@ The type system heavily relies on generic properties to provide the most accurat
|
|
|
75
75
|
|
|
76
76
|
1. If you did not complete the previous example, follow steps 1-4 to get set up.
|
|
77
77
|
2. Inside `index.ts`, define two interfaces `IQuerystring` and `IHeaders`:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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 `RequestGenericInterface` containing four named properties: `Body`, `Querystring`, `Params`, and `Headers`. The interfaces will be passed down through the route method into the route method handler `request` instance.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
78
|
+
```typescript
|
|
79
|
+
interface IQuerystring {
|
|
80
|
+
username: string;
|
|
81
|
+
password: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface IHeaders {
|
|
85
|
+
'H-Custom': string;
|
|
86
|
+
}
|
|
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 `RequestGenericInterface` containing four named properties: `Body`, `Querystring`, `Params`, and `Headers`. The interfaces will be passed down through the route method into the route method handler `request` instance.
|
|
89
|
+
```typescript
|
|
90
|
+
server.get<{
|
|
91
|
+
Querystring: IQuerystring,
|
|
92
|
+
Headers: IHeaders
|
|
93
|
+
}>('/auth', async (request, reply) => {
|
|
94
|
+
const { username, password } = request.query
|
|
95
|
+
const customerHeader = request.headers['H-Custom']
|
|
96
|
+
// do something with request data
|
|
97
|
+
|
|
98
|
+
return `logged in!`
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
101
|
4. Build and run the server code with `npm run build` and `npm run start`
|
|
102
102
|
5. Query the api
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
```bash
|
|
104
|
+
curl localhost:8080/auth?username=admin&password=Password123!
|
|
105
|
+
```
|
|
106
|
+
And it should return back `logged in!`
|
|
107
107
|
6. But wait theres more! The generic interfaces are also available inside route level hook methods. Modify the previous route by adding a `preValidation` hook:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
108
|
+
```typescript
|
|
109
|
+
server.get<{
|
|
110
|
+
Querystring: IQuerystring,
|
|
111
|
+
Headers: IHeaders
|
|
112
|
+
}>('/auth', {
|
|
113
|
+
preValidation: (request, reply, done) => {
|
|
114
|
+
const { username, password } = request.query
|
|
115
|
+
done(username !== 'admin' ? new Error('Must be admin') : undefined) // only validate `admin` account
|
|
116
|
+
}
|
|
117
|
+
}, async (request, reply) => {
|
|
118
|
+
const customerHeader = request.headers['H-Custom']
|
|
119
|
+
// do something with request data
|
|
120
|
+
return `logged in!`
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
123
|
7. Build and run and query with the `username` query string option set to anything other than `admin`. The API should now return a HTTP 500 error `{"statusCode":500,"error":"Internal Server Error","message":"Must be admin"}`
|
|
124
124
|
|
|
125
125
|
🎉 Good work, now you can define interfaces for each route and have strictly typed request and reply instances. Other parts of the Fastify type system rely on generic properties. Make sure to reference the detailed type system documentation below to learn more about what is available.
|
|
@@ -130,262 +130,262 @@ In the last example we used interfaces to define the types for the request query
|
|
|
130
130
|
|
|
131
131
|
1. If you did not complete the 'Getting Started' example, go back and follow steps 1-4 first.
|
|
132
132
|
2. Install the `json-schema-to-typescript` module:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
```
|
|
134
|
+
npm i -D json-schema-to-typescript
|
|
135
|
+
```
|
|
136
136
|
3. Create a new folder called `schemas` and add two files `headers.json` and `querystring.json`. Copy and paste the following schema definitions into the respective files:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"title": "Headers Schema",
|
|
140
|
+
"type": "object",
|
|
141
|
+
"properties": {
|
|
142
|
+
"H-Custom": { "type": "string" }
|
|
143
|
+
},
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"required": ["H-Custom"]
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"title": "Querystring Schema",
|
|
151
|
+
"type": "object",
|
|
152
|
+
"properties": {
|
|
153
|
+
"username": { "type": "string" },
|
|
154
|
+
"password": { "type": "string" }
|
|
155
|
+
},
|
|
156
|
+
"additionalProperties": false,
|
|
157
|
+
"required": ["username", "password"]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
160
|
4. Add a `compile-schemas` script to the package.json:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"scripts": {
|
|
164
|
+
"compile-schemas": "json2ts -i schemas -o types"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
`json2ts` is a CLI utility included in `json-schema-to-typescript`. `schemas` is the input path, and `types` is the output path.
|
|
169
169
|
5. Run `npm run compile-schemas`. Two new files should have been created in the `types` directory.
|
|
170
170
|
6. Update `index.ts` to have the following code:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
171
|
+
```typescript
|
|
172
|
+
import fastify from 'fastify'
|
|
173
|
+
|
|
174
|
+
// import json schemas as normal
|
|
175
|
+
import QuerystringSchema from './schemas/querystring.json'
|
|
176
|
+
import HeadersSchema from './schemas/headers.json'
|
|
177
|
+
|
|
178
|
+
// import the generated interfaces
|
|
179
|
+
import { QuerystringSchema as QuerystringSchemaInterface } from './types/querystring'
|
|
180
|
+
import { HeadersSchema as HeadersSchemaInterface } from './types/headers'
|
|
181
|
+
|
|
182
|
+
const server = fastify()
|
|
183
|
+
|
|
184
|
+
server.get<{
|
|
185
|
+
Querystring: QuerystringSchemaInterface,
|
|
186
|
+
Headers: HeadersSchemaInterface
|
|
187
|
+
}>('/auth', {
|
|
188
|
+
schema: {
|
|
189
|
+
querystring: QuerystringSchema,
|
|
190
|
+
headers: HeadersSchema
|
|
191
|
+
},
|
|
192
|
+
preValidation: (request, reply, done) => {
|
|
193
|
+
const { username, password } = request.query
|
|
194
|
+
done(username !== 'admin' ? new Error('Must be admin') : undefined)
|
|
195
|
+
}
|
|
196
|
+
}, async (request, reply) => {
|
|
197
|
+
const customerHeader = request.headers['H-Custom']
|
|
198
|
+
// do something with request data
|
|
199
|
+
return `logged in!`
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
server.route<{
|
|
203
|
+
Querystring: QuerystringSchemaInterface,
|
|
204
|
+
Headers: HeadersSchemaInterface
|
|
205
|
+
}>({
|
|
206
|
+
method: 'GET',
|
|
207
|
+
url: '/auth2',
|
|
208
|
+
schema: {
|
|
209
|
+
querystring: QuerystringSchema,
|
|
210
|
+
headers: HeadersSchema
|
|
211
|
+
},
|
|
212
|
+
preHandler: (request, reply) => {
|
|
213
|
+
const { username, password } = request.query
|
|
214
|
+
const customerHeader = request.headers['H-Custom']
|
|
215
|
+
},
|
|
216
|
+
handler: (request, reply) => {
|
|
217
|
+
const { username, password } = request.query
|
|
218
|
+
const customerHeader = request.headers['H-Custom']
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
server.listen(8080, (err, address) => {
|
|
223
|
+
if (err) {
|
|
224
|
+
console.error(err)
|
|
225
|
+
process.exit(0)
|
|
226
|
+
}
|
|
227
|
+
console.log(`Server listening at ${address}`)
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
Pay special attention to the imports at the top of this file. It might seem redundant, but you need to import both the schema files and the generated interfaces.
|
|
231
231
|
|
|
232
232
|
Great work! Now you can make use of both JSON Schemas and TypeScript definitions. If you didn't know already, defining schemas for your Fastify routes can increase their throughput! Check out the [Validation and Serialization](Validation-and-Serialization.md) documenation for more info.
|
|
233
233
|
|
|
234
234
|
Some additional notes:
|
|
235
|
-
|
|
235
|
+
- Currently, there is no type definition support for inline JSON schemas. If you can come up with a solution please open a PR!
|
|
236
236
|
|
|
237
237
|
### Plugins
|
|
238
238
|
|
|
239
|
-
One of Fastify's most distinguishable features is its extensive plugin ecosystem. Plugin types are fully supported, and take advantage of the [declaration merging]() pattern. This example is broken up into three parts: Creating a TypeScript Fastify Plugin, Creating Type Definitions for a Fastify Plugin, and Using a Fastify Plugin in a TypeScript Project.
|
|
239
|
+
One of Fastify's most distinguishable features is its extensive plugin ecosystem. Plugin types are fully supported, and take advantage of the [declaration merging]() pattern. This example is broken up into three parts: Creating a TypeScript Fastify Plugin, Creating Type Definitions for a Fastify Plugin, and Using a Fastify Plugin in a TypeScript Project.
|
|
240
240
|
|
|
241
241
|
#### Creating a TypeScript Fastify Plugin
|
|
242
242
|
|
|
243
243
|
1. Initialize a new npm project and install required dependencies
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
```bash
|
|
245
|
+
npm init -y
|
|
246
|
+
npm i fastify fastify-plugin
|
|
247
|
+
npm i -D typescript @types/node
|
|
248
|
+
```
|
|
249
249
|
2. Add a `build` script to the `"scripts"` section and `'index.d.ts'` to the `"types"` section of the `package.json` file:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"types": "index.d.ts",
|
|
253
|
+
"scripts": {
|
|
254
|
+
"build": "tsc -p tsconfig.json"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
258
|
3. Initialize a TypeScript configuration file:
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
259
|
+
```bash
|
|
260
|
+
npx typescript --init
|
|
261
|
+
```
|
|
262
|
+
Once the file is generated, enable the `"declaration"` option in the `"compilerOptions"` object.
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"compileOptions": {
|
|
266
|
+
"declaration": true
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
270
|
4. Create an `index.ts` file - this will contain the plugin code
|
|
271
271
|
5. Add the following code to `index.ts`
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
272
|
+
```typescript
|
|
273
|
+
import { FastifyPluginCallback, FastifyPluginAsync } from 'fastify'
|
|
274
|
+
import fp from 'fastify-plugin'
|
|
275
|
+
|
|
276
|
+
// using declaration merging, add your plugin props to the appropriate fastify interfaces
|
|
277
|
+
declare module 'fastify' {
|
|
278
|
+
interface FastifyRequest {
|
|
279
|
+
myPluginProp: string
|
|
280
|
+
}
|
|
281
|
+
interface FastifyReply {
|
|
282
|
+
myPluginProp: number
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// define options
|
|
287
|
+
export interface MyPluginOptions {
|
|
288
|
+
myPluginOption: string
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// define plugin using callbacks
|
|
292
|
+
const myPluginCallback: FastifyPluginCallback<MyPluginOptions> = (fastify, options, done) => {
|
|
293
|
+
fastify.decorateRequest('myPluginProp', 'super_secret_value')
|
|
294
|
+
fastify.decorateReply('myPluginProp', options.myPluginOption)
|
|
295
|
+
|
|
296
|
+
done()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// define plugin using promises
|
|
300
|
+
const myPluginAsync: FastifyPluginAsync<MyPluginOptions> = async (fastify, options) => {
|
|
301
|
+
fastify.decorateRequest('myPluginProp', 'super_secret_value')
|
|
302
|
+
fastify.decorateReply('myPluginProp', options.myPluginOption)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// export plugin using fastify-plugin
|
|
306
|
+
export default fp(myPluginCallback, '3.x')
|
|
307
|
+
// or
|
|
308
|
+
// export default fp(myPluginAsync, '3.x')
|
|
309
|
+
```
|
|
310
310
|
6. Run `npm run build` to compile the plugin code and produce both a JavaScript source file and a type definition file.
|
|
311
311
|
7. With the plugin now complete you can [publish to npm] or use it locally.
|
|
312
|
-
|
|
312
|
+
> You do not _need_ to publish your plugin to npm to use it. You can include it in a Fastify project and reference it as you would any piece of code! As a TypeScript user, make sure the declaration override exists somewhere that will be included in your project compilation so the TypeScript interpreter can process it.
|
|
313
313
|
|
|
314
314
|
#### Creating Type Definitions for a Fastify Plugin
|
|
315
315
|
|
|
316
316
|
This plugin guide is for Fastify plugins written in JavaScript. The steps outlined in this example are for adding TypeScript support for users consuming your plugin.
|
|
317
317
|
|
|
318
318
|
1. Initialize a new npm project and install required dependencies
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
319
|
+
```bash
|
|
320
|
+
npm init -y
|
|
321
|
+
npm i fastify-plugin
|
|
322
|
+
```
|
|
323
323
|
2. Create two files `index.js` and `index.d.ts`
|
|
324
324
|
3. Modify the package json to include these files under the `main` and `types` properties (the name does not have to be `index` explicitly, but it is recommended the files have the same name):
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"main": "index.js",
|
|
328
|
+
"types": "index.d.ts"
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
331
|
4. Open `index.js` and add the following code:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
function myPlugin (instance, options, next) {
|
|
337
|
-
|
|
338
|
-
// decorate the fastify instance with a custom function called myPluginFunc
|
|
339
|
-
instance.decorate('myPluginFunc', (input) => {
|
|
340
|
-
return input.toUpperCase()
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
next()
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
module.exports = fp(myPlugin, {
|
|
347
|
-
fastify: '3.x',
|
|
348
|
-
name: 'my-plugin' // this is used by fastify-plugin to derive the property name
|
|
349
|
-
})
|
|
350
|
-
```
|
|
351
|
-
5. Open `index.d.ts` and add the following code:
|
|
352
|
-
```typescript
|
|
353
|
-
import { FastifyPlugin } from 'fastify'
|
|
332
|
+
```javascript
|
|
333
|
+
// fastify-plugin is highly recommended for any plugin you write
|
|
334
|
+
const fp = require('fastify-plugin')
|
|
354
335
|
|
|
355
|
-
|
|
356
|
-
//...
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Optionally, you can add any additional exports.
|
|
360
|
-
// Here we are exporting the decorator we added.
|
|
361
|
-
export interface myPluginFunc {
|
|
362
|
-
(input: string): string
|
|
363
|
-
}
|
|
336
|
+
function myPlugin (instance, options, next) {
|
|
364
337
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
}
|
|
338
|
+
// decorate the fastify instance with a custom function called myPluginFunc
|
|
339
|
+
instance.decorate('myPluginFunc', (input) => {
|
|
340
|
+
return input.toUpperCase()
|
|
341
|
+
})
|
|
371
342
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
export const myPlugin: FastifyPlugin<PluginOptions>
|
|
343
|
+
next()
|
|
344
|
+
}
|
|
375
345
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
346
|
+
module.exports = fp(myPlugin, {
|
|
347
|
+
fastify: '3.x',
|
|
348
|
+
name: 'my-plugin' // this is used by fastify-plugin to derive the property name
|
|
349
|
+
})
|
|
350
|
+
```
|
|
351
|
+
5. Open `index.d.ts` and add the following code:
|
|
352
|
+
```typescript
|
|
353
|
+
import { FastifyPlugin } from 'fastify'
|
|
354
|
+
|
|
355
|
+
interface PluginOptions {
|
|
356
|
+
//...
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Optionally, you can add any additional exports.
|
|
360
|
+
// Here we are exporting the decorator we added.
|
|
361
|
+
export interface myPluginFunc {
|
|
362
|
+
(input: string): string
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Most importantly, use declaration merging to add the custom property to the Fastify type system
|
|
366
|
+
declare module 'fastify' {
|
|
367
|
+
interface FastifyInstance {
|
|
368
|
+
myPluginFunc: myPluginFunc
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// fastify-plugin automatically adds named export, so be sure to add also this type
|
|
373
|
+
// the variable name is derived from `options.name` property if `module.exports.myPlugin` is missing
|
|
374
|
+
export const myPlugin: FastifyPlugin<PluginOptions>
|
|
375
|
+
|
|
376
|
+
// fastify-plugin automatically adds `.default` property to the exported plugin. See the note below
|
|
377
|
+
export default myPlugin
|
|
378
|
+
```
|
|
379
379
|
|
|
380
380
|
__Note__: [fastify-plugin](https://github.com/fastify/fastify-plugin) v2.3.0 and newer, automatically adds `.default` property and a named export to the exported plugin. Be sure to `export default` and `export const myPlugin` in your typings to provide the best developer experience. For a complete example you can check out [fastify-swagger](https://github.com/fastify/fastify-swagger/blob/master/index.d.ts).
|
|
381
381
|
|
|
382
|
-
With those files completed, the plugin is now ready to be consumed by any TypeScript project!
|
|
382
|
+
With those files completed, the plugin is now ready to be consumed by any TypeScript project!
|
|
383
383
|
|
|
384
384
|
The Fastify plugin system enables developers to decorate the Fastify instance, and the request/reply instances. For more information check out this blog post on [Declaration Merging and Generic Inheritance](https://dev.to/ethanarrowood/is-declaration-merging-and-generic-inheritance-at-the-same-time-impossible-53cp).
|
|
385
385
|
|
|
386
386
|
#### Using a Plugin
|
|
387
387
|
|
|
388
|
-
Using a Fastify plugin in TypeScript is just as easy as using one in JavaScript. Import the plugin with `import/from` and you're all set -- except there is one exception users should be aware of.
|
|
388
|
+
Using a Fastify plugin in TypeScript is just as easy as using one in JavaScript. Import the plugin with `import/from` and you're all set -- except there is one exception users should be aware of.
|
|
389
389
|
|
|
390
390
|
Fastify plugins use declaration merging to modify existing Fastify type interfaces (check out the previous two examples for more details). Declaration merging is not very _smart_, meaning if the plugin type definition for a plugin is within the scope of the TypeScript interpreter, then the plugin types will be included **regardless** of if the plugin is being used or not. This is an unfortunate limitation of using TypeScript and is unavoidable as of right now.
|
|
391
391
|
|
|
@@ -408,54 +408,54 @@ All `http`, `https`, and `http2` types are inferred from `@types/node`
|
|
|
408
408
|
The Fastify API is powered by the `fastify()` method. In JavaScript you would import it using `const fastify = require('fastify')`. In TypeScript it is recommended to use the `import/from` syntax instead so types can be resolved. There are a couple supported import methods with the Fastify type system.
|
|
409
409
|
|
|
410
410
|
1. `import fastify from 'fastify'`
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
411
|
+
- Types are resolved but not accessible using dot notation
|
|
412
|
+
- Example:
|
|
413
|
+
```typescript
|
|
414
|
+
import fastify from 'fastify'
|
|
415
|
+
|
|
416
|
+
const f = fastify()
|
|
417
|
+
f.listen(8080, () => { console.log('running') })
|
|
418
|
+
```
|
|
419
|
+
- Gain access to types with destructuring:
|
|
420
|
+
```typescript
|
|
421
|
+
import fastify, { FastifyInstance } from 'fastify'
|
|
422
|
+
|
|
423
|
+
const f: FastifyInstance = fastify()
|
|
424
|
+
f.listen(8080, () => { console.log('running') })
|
|
425
|
+
```
|
|
426
|
+
- Destructuring also works for the main API method:
|
|
427
|
+
```typescript
|
|
428
|
+
import { fastify, FastifyInstance } from 'fastify'
|
|
429
|
+
|
|
430
|
+
const f: FastifyInstance = fastify()
|
|
431
|
+
f.listen(8080, () => { console.log('running') })
|
|
432
|
+
```
|
|
433
433
|
2. `import * as Fastify from 'fastify'`
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
434
|
+
- Types are resolved and accessible using dot notation
|
|
435
|
+
- Calling the main Fastify API method requires a slightly different syntax (see example)
|
|
436
|
+
- Example:
|
|
437
|
+
```typescript
|
|
438
|
+
import * as Fastify from 'fastify'
|
|
439
|
+
|
|
440
|
+
const f: Fastify.FastifyInstance = Fastify.fastify()
|
|
441
|
+
f.listen(8080, () => { console.log('running') })
|
|
442
|
+
```
|
|
443
443
|
3. `const fastify = require('fastify')`
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
444
|
+
- This syntax is valid and will import fastify as expected; however, types will **not** be resolved
|
|
445
|
+
- Example:
|
|
446
|
+
```typescript
|
|
447
|
+
const fastify = require('fastify')
|
|
448
|
+
|
|
449
|
+
const f = fastify()
|
|
450
|
+
f.listen(8080, () => { console.log('running') })
|
|
451
|
+
```
|
|
452
|
+
- Destructuring is still supported, but will also not resolve types
|
|
453
|
+
```typescript
|
|
454
|
+
const { fastify } = require('fastify')
|
|
455
|
+
|
|
456
|
+
const f = fastify()
|
|
457
|
+
f.listen(8080, () => { console.log('running') })
|
|
458
|
+
```
|
|
459
459
|
|
|
460
460
|
#### Generics
|
|
461
461
|
|
|
@@ -463,7 +463,7 @@ Many type definitions share the same generic parameters; they are all documented
|
|
|
463
463
|
|
|
464
464
|
Most definitions depend on `@node/types` modules `http`, `https`, and `http2`
|
|
465
465
|
|
|
466
|
-
##### RawServer
|
|
466
|
+
##### RawServer
|
|
467
467
|
Underlying Node.js server type
|
|
468
468
|
|
|
469
469
|
Default: `http.Server`
|
|
@@ -482,7 +482,7 @@ Constraints: `http.IncomingMessage`, `http2.Http2ServerRequest`
|
|
|
482
482
|
Enforced by: [`RawServer`][RawServerGeneric]
|
|
483
483
|
|
|
484
484
|
##### RawReply
|
|
485
|
-
Underlying Node.js response type
|
|
485
|
+
Underlying Node.js response type
|
|
486
486
|
|
|
487
487
|
Default: [`RawReplyDefaultExpression`][RawReplyDefaultExpression]
|
|
488
488
|
|
|
@@ -524,33 +524,33 @@ Check out the Learn By Example - [Getting Started](#getting-started) example for
|
|
|
524
524
|
###### Example 2: HTTPS sever
|
|
525
525
|
|
|
526
526
|
1. Create the following imports from `@types/node` and `fastify`
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
527
|
+
```typescript
|
|
528
|
+
import fs from 'fs'
|
|
529
|
+
import path from 'path'
|
|
530
|
+
import fastify from 'fastify'
|
|
531
|
+
```
|
|
532
532
|
2. Follow the steps in this official [Node.js https server guide](https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTPS-server/) to create the `key.pem` and `cert.pem` files
|
|
533
533
|
3. Instantiate a Fastify https server and add a route:
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
534
|
+
```typescript
|
|
535
|
+
const server = fastify({
|
|
536
|
+
https: {
|
|
537
|
+
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
|
|
538
|
+
cert: fs.readFileSync(path.join(__dirname, 'cert.pem'))
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
server.get('/', async function (request, reply) {
|
|
543
|
+
return { hello: 'world' }
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
server.listen(8080, (err, address) => {
|
|
547
|
+
if (err) {
|
|
548
|
+
console.error(err)
|
|
549
|
+
process.exit(0)
|
|
550
|
+
}
|
|
551
|
+
console.log(`Server listening at ${address}`)
|
|
552
|
+
})
|
|
553
|
+
```
|
|
554
554
|
4. Build and run! Test your server out by querying with: `curl -k https://localhost:8080`
|
|
555
555
|
|
|
556
556
|
###### Example 3: HTTP2 server
|
|
@@ -612,19 +612,19 @@ server.get('/', async (request, reply) => {
|
|
|
612
612
|
|
|
613
613
|
---
|
|
614
614
|
|
|
615
|
-
##### fastify.HTTPMethods
|
|
615
|
+
##### fastify.HTTPMethods
|
|
616
616
|
[src](./../types/utils.d.ts#L8)
|
|
617
617
|
|
|
618
618
|
Union type of: `'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS'`
|
|
619
619
|
|
|
620
|
-
##### fastify.RawServerBase
|
|
620
|
+
##### fastify.RawServerBase
|
|
621
621
|
[src](./../types/utils.d.ts#L13)
|
|
622
622
|
|
|
623
623
|
Dependant on `@types/node` modules `http`, `https`, `http2`
|
|
624
624
|
|
|
625
625
|
Union type of: `http.Server | https.Server | http2.Http2Server | http2.Http2SecureServer`
|
|
626
626
|
|
|
627
|
-
##### fastify.RawServerDefault
|
|
627
|
+
##### fastify.RawServerDefault
|
|
628
628
|
[src](./../types/utils.d.ts#L18)
|
|
629
629
|
|
|
630
630
|
Dependant on `@types/node` modules `http`
|
|
@@ -645,7 +645,7 @@ See the main [fastify][Fastify] method type definition section for examples on i
|
|
|
645
645
|
|
|
646
646
|
[src](../types/instance.d.ts#L16)
|
|
647
647
|
|
|
648
|
-
Interface that represents the Fastify server object. This is the returned server instance from the [`fastify()`][Fastify] method. This type is an interface so it can be extended via [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) if your code makes use of the `decorate` method.
|
|
648
|
+
Interface that represents the Fastify server object. This is the returned server instance from the [`fastify()`][Fastify] method. This type is an interface so it can be extended via [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) if your code makes use of the `decorate` method.
|
|
649
649
|
|
|
650
650
|
Through the use of generic cascading, all methods attached to the instance inherit the generic properties from instantiation. This means that by specifying the server, request, or reply types, all methods will know how to type those objects.
|
|
651
651
|
|
|
@@ -655,7 +655,7 @@ Check out the main [Learn by Example](#learn-by-example) section for detailed gu
|
|
|
655
655
|
|
|
656
656
|
#### Request
|
|
657
657
|
|
|
658
|
-
##### fastify.FastifyRequest<[RequestGeneric][FastifyRequestGenericInterface], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
|
|
658
|
+
##### fastify.FastifyRequest<[RequestGeneric][FastifyRequestGenericInterface], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
|
|
659
659
|
[src](./../types/request.d.ts#L15)
|
|
660
660
|
|
|
661
661
|
This interface contains properties of Fastify request object. The properties added here disregard what kind of request object (http vs http2) and disregard what route level it is serving; thus calling `request.body` inside a GET request will not throw an error (but good luck sending a GET request with a body 😉).
|
|
@@ -770,7 +770,7 @@ declare module 'fastify' {
|
|
|
770
770
|
}
|
|
771
771
|
```
|
|
772
772
|
|
|
773
|
-
##### fastify.RawReplyDefaultExpression<[RawServer][RawServerGeneric]>
|
|
773
|
+
##### fastify.RawReplyDefaultExpression<[RawServer][RawServerGeneric]>
|
|
774
774
|
[src](./../types/utils.d.ts#L27)
|
|
775
775
|
|
|
776
776
|
Dependant on `@types/node` modules `http`, `https`, `http2`
|
|
@@ -971,7 +971,7 @@ A method for checking the existence of a type parser of a certain content type
|
|
|
971
971
|
|
|
972
972
|
FastifyError is a custom error object that includes status code and validation results.
|
|
973
973
|
|
|
974
|
-
It extends the Node.js `Error` type, and adds two additional, optional properties: `statusCode: number` and `validation: ValiationResult[]`.
|
|
974
|
+
It extends the Node.js `Error` type, and adds two additional, optional properties: `statusCode: number` and `validation: ValiationResult[]`.
|
|
975
975
|
|
|
976
976
|
##### fastify.ValidationResult
|
|
977
977
|
|
|
@@ -1000,8 +1000,8 @@ Notice: in the `onRequest` hook, request.body will always be null, because the b
|
|
|
1000
1000
|
preParsing` is the second hook to be executed in the request lifecycle. The previous hook was `onRequest`, the next hook will be `preValidation`.
|
|
1001
1001
|
|
|
1002
1002
|
Notice: in the `preParsing` hook, request.body will always be null, because the body parsing happens before the `preValidation` hook.
|
|
1003
|
-
|
|
1004
|
-
Notice: you should also add `receivedEncodedLength` property to the returned stream. This property is used to correctly match the request payload with the `Content-Length` header value. Ideally, this property should be updated on each received chunk.
|
|
1003
|
+
|
|
1004
|
+
Notice: you should also add `receivedEncodedLength` property to the returned stream. This property is used to correctly match the request payload with the `Content-Length` header value. Ideally, this property should be updated on each received chunk.
|
|
1005
1005
|
|
|
1006
1006
|
##### fastify.preValidationHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
|
|
1007
1007
|
|