UNPKG

16.9 kBMarkdownView Raw
1<h1 align="center">Fastify</h1>
2
3## Routes
4
5The routes methods will configure the endpoints of your application.
6You have two ways to declare a route with Fastify, the shorthand method and the full declaration.
7
8- [Full Declaration](#full-declaration)
9- [Route Options](#options)
10- [Shorthand Declaration](#shorthand-declaration)
11- [URL Parameters](#url-building)
12- [Use `async`/`await`](#async-await)
13- [Promise resolution](#promise-resolution)
14- [Route Prefixing](#route-prefixing)
15- Logs
16 - [Custom Log Level](#custom-log-level)
17 - [Custom Log Serializer](#custom-log-serializer)
18- [Route handler configuration](#routes-config)
19- [Route's Versioning](#version)
20
21<a name="full-declaration"></a>
22### Full declaration
23
24```js
25fastify.route(options)
26```
27
28<a name="options"></a>
29### Routes option
30
31* `method`: currently it supports `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`, `'POST'`, `'PUT'` and `'OPTIONS'`. It could also be an array of methods.
32* `url`: the path of the url to match this route (alias: `path`).
33* `schema`: an object containing the schemas for the request and response.
34They need to be in
35 [JSON Schema](http://json-schema.org/) format, check [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md) for more info.
36
37 * `body`: validates the body of the request if it is a POST or a
38 PUT.
39 * `querystring` or `query`: validates the querystring. This can be a complete JSON
40 Schema object, with the property `type` of `object` and `properties` object of parameters, or
41 simply the values of what would be contained in the `properties` object as shown below.
42 * `params`: validates the params.
43 * `response`: filter and generate a schema for the response, setting a
44 schema allows us to have 10-20% more throughput.
45* `attachValidation`: attach `validationError` to request, if there is a schema validation error, instead of sending the error to the error handler.
46* `onRequest(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#onrequest) as soon that a request is received, it could also be an array of functions.
47* `preParsing(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#preparsing) called before parsing the request, it could also be an array of functions.
48* `preValidation(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#prevalidation) called after the shared `preValidation` hooks, useful if you need to perform authentication at route level for example, it could also be an array of functions.
49* `preHandler(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#prehandler) called just before the request handler, it could also be an array of functions.
50* `preSerialization(request, reply, payload, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#preserialization) called just before the serialization, it could also be an array of functions.
51* `onSend(request, reply, payload, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#route-hooks) called right before a response is sent, it could also be an array of functions.
52* `onResponse(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#onresponse) called when a response has been sent, so you will not be able to send more data to the client. It could also be an array of functions.
53* `handler(request, reply)`: the function that will handle this request.
54* `schemaCompiler(schema)`: the function that build the schema for the validations. See [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#schema-compiler)
55* `bodyLimit`: prevents the default JSON body parser from parsing request bodies larger than this number of bytes. Must be an integer. You may also set this option globally when first creating the Fastify instance with `fastify(options)`. Defaults to `1048576` (1 MiB).
56* `logLevel`: set log level for this route. See below.
57* `logSerializers`: set serializers to log for this route.
58* `config`: object used to store custom configuration.
59* `version`: a [semver](http://semver.org/) compatible string that defined the version of the endpoint. [Example](https://github.com/fastify/fastify/blob/master/docs/Routes.md#version).
60* `prefixTrailingSlash`: string used to determine how to handle passing `/` as a route with a prefix.
61 * `both` (default): Will register both `/prefix` and `/prefix/`.
62 * `slash`: Will register only `/prefix/`.
63 * `no-slash`: Will register only `/prefix`.
64
65 `request` is defined in [Request](https://github.com/fastify/fastify/blob/master/docs/Request.md).
66
67 `reply` is defined in [Reply](https://github.com/fastify/fastify/blob/master/docs/Reply.md).
68
69
70Example:
71```js
72fastify.route({
73 method: 'GET',
74 url: '/',
75 schema: {
76 querystring: {
77 name: { type: 'string' },
78 excitement: { type: 'integer' }
79 },
80 response: {
81 200: {
82 type: 'object',
83 properties: {
84 hello: { type: 'string' }
85 }
86 }
87 }
88 },
89 handler: function (request, reply) {
90 reply.send({ hello: 'world' })
91 }
92})
93```
94
95<a name="shorthand-declaration"></a>
96### Shorthand declaration
97The above route declaration is more *Hapi*-like, but if you prefer an *Express/Restify* approach, we support it as well:<br>
98`fastify.get(path, [options], handler)`<br>
99`fastify.head(path, [options], handler)`<br>
100`fastify.post(path, [options], handler)`<br>
101`fastify.put(path, [options], handler)`<br>
102`fastify.delete(path, [options], handler)`<br>
103`fastify.options(path, [options], handler)`<br>
104`fastify.patch(path, [options], handler)`
105
106Example:
107```js
108const opts = {
109 schema: {
110 response: {
111 200: {
112 type: 'object',
113 properties: {
114 hello: { type: 'string' }
115 }
116 }
117 }
118 }
119}
120fastify.get('/', opts, (request, reply) => {
121 reply.send({ hello: 'world' })
122})
123```
124
125`fastify.all(path, [options], handler)` will add the same handler to all the supported methods.
126
127The handler may also be supplied via the `options` object:
128```js
129const opts = {
130 schema: {
131 response: {
132 200: {
133 type: 'object',
134 properties: {
135 hello: { type: 'string' }
136 }
137 }
138 }
139 },
140 handler (request, reply) {
141 reply.send({ hello: 'world' })
142 }
143}
144fastify.get('/', opts)
145```
146
147> Note: if the handler is specified in both the `options` and as the third parameter to the shortcut method then throws duplicate `handler` error.
148
149<a name="url-building"></a>
150### Url building
151Fastify supports both static and dynamic urls.<br>
152To register a **parametric** path, use the *colon* before the parameter name. For **wildcard** use the *star*.
153*Remember that static routes are always checked before parametric and wildcard.*
154
155```js
156// parametric
157fastify.get('/example/:userId', (request, reply) => {})
158fastify.get('/example/:userId/:secretToken', (request, reply) => {})
159
160// wildcard
161fastify.get('/example/*', (request, reply) => {})
162```
163
164Regular expression routes are supported as well, but pay attention, RegExp are very expensive in term of performance!
165```js
166// parametric with regexp
167fastify.get('/example/:file(^\\d+).png', (request, reply) => {})
168```
169
170It's possible to define more than one parameter within the same couple of slash ("/"). Such as:
171```js
172fastify.get('/example/near/:lat-:lng/radius/:r', (request, reply) => {})
173```
174*Remember in this case to use the dash ("-") as parameters separator.*
175
176Finally it's possible to have multiple parameters with RegExp.
177```js
178fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', (request, reply) => {})
179```
180In this case as parameter separator it's possible to use whatever character is not matched by the regular expression.
181
182Having a route with multiple parameters may affect negatively the performance, so prefer single parameter approach whenever possible, especially on routes which are on the hot path of your application.
183If you are interested in how we handle the routing, checkout [find-my-way](https://github.com/delvedor/find-my-way).
184
185<a name="async-await"></a>
186### Async Await
187Are you an `async/await` user? We have you covered!
188```js
189fastify.get('/', options, async function (request, reply) {
190 var data = await getData()
191 var processed = await processData(data)
192 return processed
193})
194```
195
196As you can see we are not calling `reply.send` to send back the data to the user. You just need to return the body and you are done!
197
198If you need it you can also send back the data to the user with `reply.send`.
199```js
200fastify.get('/', options, async function (request, reply) {
201 var data = await getData()
202 var processed = await processData(data)
203 reply.send(processed)
204})
205```
206
207If the route is wrapping a callback-based API that will call
208`reply.send()` outside of the promise chain, it is possible to `await reply`:
209
210```js
211fastify.get('/', options, async function (request, reply) {
212 setImmediate(() => {
213 reply.send({ hello: 'world' })
214 })
215 await reply
216})
217```
218
219Returning reply also works:
220
221```js
222fastify.get('/', options, async function (request, reply) {
223 setImmediate(() => {
224 reply.send({ hello: 'world' })
225 })
226 return reply
227})
228```
229
230**Warning:**
231* When using both `return value` and `reply.send(value)` at the same time, the first one that happens takes precedence, the second value will be discarded, and a *warn* log will also be emitted because you tried to send a response twice.
232* You can't return `undefined`. For more details read [promise-resolution](#promise-resolution).
233
234<a name="promise-resolution"></a>
235### Promise resolution
236
237If your handler is an `async` function or returns a promise, you should be aware of a special behaviour which is necessary to support the callback and promise control-flow. If the handler's promise is resolved with `undefined`, it will be ignored causing the request to hang and an *error* log to be emitted.
238
2391. If you want to use `async/await` or promises but respond a value with `reply.send`:
240 - **Don't** `return` any value.
241 - **Don't** forget to call `reply.send`.
2422. If you want to use `async/await` or promises:
243 - **Don't** use `reply.send`.
244 - **Don't** return `undefined`.
245
246In this way, we can support both `callback-style` and `async-await`, with the minimum trade-off. In spite of so much freedom we highly recommend to go with only one style because error handling should be handled in a consistent way within your application.
247
248**Notice**: Every async function returns a promise by itself.
249
250<a name="route-prefixing"></a>
251### Route Prefixing
252Sometimes you need to maintain two or more different versions of the same api, a classic approach is to prefix all the routes with the api version number, `/v1/user` for example.
253Fastify offers you a fast and smart way to create different version of the same api without changing all the route names by hand, *route prefixing*. Let's see how it works:
254
255```js
256// server.js
257const fastify = require('fastify')()
258
259fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
260fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
261
262fastify.listen(3000)
263```
264
265```js
266// routes/v1/users.js
267module.exports = function (fastify, opts, done) {
268 fastify.get('/user', handler_v1)
269 done()
270}
271```
272
273```js
274// routes/v2/users.js
275module.exports = function (fastify, opts, done) {
276 fastify.get('/user', handler_v2)
277 done()
278}
279```
280Fastify will not complain because you are using the same name for two different routes, because at compilation time it will handle the prefix automatically *(this also means that the performance will not be affected at all!)*.
281
282Now your clients will have access to the following routes:
283- `/v1/user`
284- `/v2/user`
285
286You can do this as many times as you want, it works also for nested `register` and routes parameter are supported as well.
287Be aware that if you use [`fastify-plugin`](https://github.com/fastify/fastify-plugin) this option won't work.
288
289#### Handling of / route inside prefixed plugins
290
291The `/` route has a different behavior depending if the prefix ends with
292`/` or not. As an example, if we consider a prefix `/something/`,
293adding a `/` route will only match `/something/`. If we consider a
294prefix `/something`, adding a `/` route will match both `/something` 
295and `/something/`.
296
297See the `prefixTrailingSlash` route option above to change this behaviour.
298
299<a name="custom-log-level"></a>
300### Custom Log Level
301It could happen that you need different log levels in your routes, Fastify achieves this in a very straightforward way.<br/>
302You just need to pass the option `logLevel` to the plugin option or the route option with the [value](https://github.com/pinojs/pino/blob/master/docs/API.md#discussion-3) that you need.
303
304Be aware that if you set the `logLevel` at plugin level, also the [`setNotFoundHandler`](https://github.com/fastify/fastify/blob/master/docs/Server.md#setnotfoundhandler) and [`setErrorHandler`](https://github.com/fastify/fastify/blob/master/docs/Server.md#seterrorhandler) will be affected.
305
306```js
307// server.js
308const fastify = require('fastify')({ logger: true })
309
310fastify.register(require('./routes/user'), { logLevel: 'warn' })
311fastify.register(require('./routes/events'), { logLevel: 'debug' })
312
313fastify.listen(3000)
314```
315
316Or you can directly pass it to a route:
317```js
318fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
319 reply.send({ hello: 'world' })
320})
321```
322*Remember that the custom log level is applied only to the routes, and not to the global Fastify Logger, accessible with `fastify.log`*
323
324<a name="custom-log-serializer"></a>
325### Custom Log Serializer
326
327In some context, you may need to log a large object but it could be a waste of resources for some routes. In this case, you can define some [`serializer`](https://github.com/pinojs/pino/blob/master/docs/api.md#bindingsserializers-object) and attach them in the right context!
328
329```js
330const fastify = require('fastify')({ logger: true })
331
332fastify.register(require('./routes/user'), {
333 logSerializers: {
334 user: (value) => `My serializer one - ${value.name}`
335 }
336})
337fastify.register(require('./routes/events'), {
338 logSerializers: {
339 user: (value) => `My serializer two - ${value.name} ${value.surname}`
340 }
341})
342
343fastify.listen(3000)
344```
345
346You can inherit serializers by context:
347
348```js
349const fastify = Fastify({
350 logger: {
351 level: 'info',
352 serializers: {
353 user (req) {
354 return {
355 method: req.method,
356 url: req.url,
357 headers: req.headers,
358 hostname: req.hostname,
359 remoteAddress: req.ip,
360 remotePort: req.connection.remotePort
361 }
362 }
363 }
364 }
365})
366
367fastify.register(context1, {
368 logSerializers: {
369 user: value => `My serializer father - ${value}`
370 }
371})
372
373async function context1 (fastify, opts) {
374 fastify.get('/', (req, reply) => {
375 req.log.info({ user: 'call father serializer', key: 'another key' })
376 // shows: { user: 'My serializer father - call father serializer', key: 'another key' }
377 reply.send({})
378 })
379}
380
381fastify.listen(3000)
382```
383
384<a name="routes-config"></a>
385### Config
386Registering a new handler, you can pass a configuration object to it and retrieve it in the handler.
387
388```js
389// server.js
390const fastify = require('fastify')()
391
392function handler (req, reply) {
393 reply.send(reply.context.config.output)
394}
395
396fastify.get('/en', { config: { output: 'hello world!' } }, handler)
397fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
398
399fastify.listen(3000)
400```
401
402<a name="version"></a>
403### Version
404
405#### Default
406If needed you can provide a version option, which will allow you to declare multiple versions of the same route. The versioning should follow the [semver](http://semver.org/) specification.<br/>
407Fastify will automatically detect the `Accept-Version` header and route the request accordingly (advanced ranges and pre-releases currently are not supported).<br/>
408*Be aware that using this feature will cause a degradation of the overall performances of the router.*
409
410```js
411fastify.route({
412 method: 'GET',
413 url: '/',
414 version: '1.2.0',
415 handler: function (request, reply) {
416 reply.send({ hello: 'world' })
417 }
418})
419
420fastify.inject({
421 method: 'GET',
422 url: '/',
423 headers: {
424 'Accept-Version': '1.x' // it could also be '1.2.0' or '1.2.x'
425 }
426}, (err, res) => {
427 // { hello: 'world' }
428})
429```
430
431If you declare multiple versions with the same major or minor, Fastify will always choose the highest compatible with the `Accept-Version` header value.<br/>
432If the request will not have the `Accept-Version` header, a 404 error will be returned.
433
434#### Custom
435It's possible to define a custom versioning logic. This can be done through the [`versioning`](https://github.com/fastify/fastify/blob/master/docs/Server.md#versioning) configuration, when creating a fastify server instance.