UNPKG

9 kBMarkdownView Raw
1<h1 align="center">Fastify</h1>
2
3<a id="typescript"></a>
4## TypeScript
5Fastify is shipped with a typings file, but it still require to install `@types/node`, depending on the Node.js version that you are using.
6
7## Types support
8We do care about the TypeScript community, but the framework is written in plain JavaScript and currently no one of the core team is a TypeScript user while only one of the collaborators is.
9We do our best to have the typing updated with the latest version of the API, but *it can happen* that the typings are not in sync.<br/>
10Luckly this is Open Source and you can contribute to fix them, we will be very happy to accept the fix and release it as soon as possible as a patch release. Checkout the [contributing](#contributing) rules!
11
12Plugins may or may not include typings. See [Plugin Types](#plugin-types) for more information.
13
14## Example
15This example TypeScript app closely aligns with the JavaScript examples:
16
17```ts
18import * as fastify from 'fastify'
19import { Server, IncomingMessage, ServerResponse } from 'http'
20
21// Create a http server. We pass the relevant typings for our http version used.
22// By passing types we get correctly typed access to the underlying http objects in routes.
23// If using http2 we'd pass <http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>
24const server: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse> = fastify({})
25
26const opts: fastify.RouteShorthandOptions = {
27 schema: {
28 response: {
29 200: {
30 type: 'object',
31 properties: {
32 pong: {
33 type: 'string'
34 }
35 }
36 }
37 }
38 }
39}
40
41server.get('/ping', opts, (request, reply) => {
42 console.log(reply.res) // this is the http.ServerResponse with correct typings!
43 reply.code(200).send({ pong: 'it worked!' })
44})
45```
46
47<a id="generic-parameters"></a>
48## Generic Parameters
49Since you can validate the querystring, params, body, and headers, you can also override the default types of those values on the request interface:
50
51```ts
52import * as fastify from 'fastify'
53
54const server = fastify({})
55
56interface Query {
57 foo?: number
58}
59
60interface Params {
61 bar?: string
62}
63
64interface Body {
65 baz?: string
66}
67
68interface Headers {
69 a?: string
70}
71
72const opts: fastify.RouteShorthandOptions = {
73 schema: {
74 querystring: {
75 type: 'object',
76 properties: {
77 foo: {
78 type: 'number'
79 }
80 }
81 },
82 params: {
83 type: 'object',
84 properties: {
85 bar: {
86 type: 'string'
87 }
88 }
89 },
90 body: {
91 type: 'object',
92 properties: {
93 baz: {
94 type: 'string'
95 }
96 }
97 },
98 headers: {
99 type: 'object',
100 properties: {
101 a: {
102 type: 'string'
103 }
104 }
105 }
106 }
107}
108
109server.get<Query, Params, Body, Headers>('/ping/:bar', opts, (request, reply) => {
110 console.log(request.query) // this is of type Query!
111 console.log(request.params) // this is of type Params!
112 console.log(request.body) // this is of type Body!
113 console.log(request.headers) // this is of type Headers!
114 reply.code(200).send({ pong: 'it worked!' })
115})
116```
117
118All generic types are optional, so you can also pass types for the parts you validate with schemas:
119
120```ts
121import * as fastify from 'fastify'
122
123const server = fastify({})
124
125interface Params {
126 bar?: string
127}
128
129const opts: fastify.RouteShorthandOptions = {
130 schema: {
131 params: {
132 type: 'object',
133 properties: {
134 bar: {
135 type: 'string'
136 }
137 }
138 },
139 }
140}
141
142server.get<fastify.DefaultQuery, Params, unknown>('/ping/:bar', opts, (request, reply) => {
143 console.log(request.query) // this is of type fastify.DefaultQuery!
144 console.log(request.params) // this is of type Params!
145 console.log(request.body) // this is of type unknown!
146 console.log(request.headers) // this is of type fastify.DefaultHeader because typescript will use the default type value!
147 reply.code(200).send({ pong: 'it worked!' })
148})
149
150// Given that you haven't validated the querystring, body, or headers, it would be best
151// to type those params as 'unknown'. However, it's up to you. The example below is the
152// best way to prevent you from shooting yourself in the foot. In other words, don't
153// use values you haven't validated.
154server.get<unknown, Params, unknown, unknown>('/ping/:bar', opts, (request, reply) => {
155 console.log(request.query) // this is of type unknown!
156 console.log(request.params) // this is of type Params!
157 console.log(request.body) // this is of type unknown!
158 console.log(request.headers) // this is of type unknown!
159 reply.code(200).send({ pong: 'it worked!' })
160})
161```
162
163<a id="http-prototypes"></a>
164## HTTP Prototypes
165By default, fastify will determine which version of http is being used based on the options you pass to it. If for any
166reason you need to override this you can do so as shown below:
167
168```ts
169interface CustomIncomingMessage extends http.IncomingMessage {
170 getClientDeviceType: () => string
171}
172
173// Passing overrides for the http prototypes to fastify
174const server: fastify.FastifyInstance<http.Server, CustomIncomingMessage, http.ServerResponse> = fastify()
175
176server.get('/ping', (request, reply) => {
177 // Access our custom method on the http prototype
178 const clientDeviceType = request.raw.getClientDeviceType()
179
180 reply.send({ clientDeviceType: `you called this endpoint from a ${clientDeviceType}` })
181})
182```
183
184In this example we pass a modified `http.IncomingMessage` interface since it has been extended elsewhere in our
185application.
186
187
188<a id="contributing"></a>
189## Contributing
190TypeScript related changes can be considered to fall into one of two categories:
191
192* Core - The typings bundled with fastify
193* Plugins - Fastify ecosystem plugins
194
195Make sure to read our [`CONTRIBUTING.md`](https://github.com/fastify/fastify/blob/master/CONTRIBUTING.md) file before getting started to make sure things go smoothly!
196
197<a id="core-types"></a>
198### Core Types
199When updating core types you should make a PR to this repository. Ensure you:
200
2011. Update `examples/typescript-server.ts` to reflect the changes (if necessary)
2022. Update `test/types/index.ts` to validate changes work as expected
203
204<a id="plugin-types"></a>
205### Plugin Types
206
207Plugins maintained by and orginized under the fastify organization on GitHub should ship with typings just like fastify itself does.
208Some plugins already include typings but many do not. We are happy to accept contributions to those plugins without any typings, see [fastify-cors](https://github.com/fastify/fastify-cors) for an example of a plugin that comes with it's own typings.
209
210Typings for third-party-plugins may either be included with the plugin or hosted on DefinitelyTyped. Remember, if you author a plugin to either include typings or publish them on DefinitelyTyped! Information of how to install typings from DefinitelyTyped can be found [here](https://github.com/DefinitelyTyped/DefinitelyTyped#npm).
211
212Some types might not be available yet, so don't be shy about contributing.
213
214<a id="authoring-plugin-types"></a>
215### Authoring Plugin Types
216Typings for many plugins that extend the `FastifyRequest`, `FastifyReply` or `FastifyInstance` objects can be achieved as shown below.
217
218This code shows the typings for the `fastify-static` plugin.
219
220```ts
221// require fastify typings
222import fastify = require("fastify");
223// require necessary http typings
224import { Server, IncomingMessage, ServerResponse } from "http";
225
226// extend fastify typings
227declare module "fastify" {
228 interface FastifyReply<HttpResponse> {
229 sendFile(filename: string): FastifyReply<HttpResponse>;
230 }
231}
232
233// declare plugin type using fastify.Plugin
234declare const fastifyStatic: fastify.Plugin<Server, IncomingMessage, ServerResponse, {
235 root: string;
236 prefix?: string;
237 serve?: boolean;
238 decorateReply?: boolean;
239 schemaHide?: boolean;
240 setHeaders?: (...args: any[]) => void;
241 acceptRanges?: boolean;
242 cacheControl?: boolean;
243 dotfiles?: boolean;
244 etag?: boolean;
245 extensions?: string[];
246 immutable?: boolean;
247 index?: string[];
248 lastModified?: boolean;
249 maxAge?: string | number;
250}>;
251
252// export plugin type
253export = fastifyStatic;
254```
255
256Now you are good to go and could use the plugin like so:
257
258```ts
259import * as Fastify from 'fastify'
260import * as fastifyStatic from 'fastify-static'
261
262const app = Fastify()
263
264// the options here are type-checked
265app.register(fastifyStatic, {
266 acceptRanges: true,
267 cacheControl: true,
268 decorateReply: true,
269 dotfiles: true,
270 etag: true,
271 extensions: ['.js'],
272 immutable: true,
273 index: ['1'],
274 lastModified: true,
275 maxAge: '',
276 prefix: '',
277 root: '',
278 schemaHide: true,
279 serve: true,
280 setHeaders: (res, pathName) => {
281 res.setHeader('some-header', pathName)
282 }
283})
284
285app.get('/file', (request, reply) => {
286 // using newly defined function on FastifyReply
287 reply.sendFile('some-file-name')
288})
289```
290
291Adding typings to all our plugins is a community effort so feel free to contribute!