Spaces:
Runtime error
Runtime error
File size: 26,939 Bytes
23ac194 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 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 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 |
<h1 align="center">Fastify</h1>
## Routes
The route methods will configure the endpoints of your application. You have two
ways to declare a route with Fastify: the shorthand method and the full
declaration.
- [Full declaration](#full-declaration)
- [Routes options](#routes-options)
- [Shorthand declaration](#shorthand-declaration)
- [Url building](#url-building)
- [Async Await](#async-await)
- [Promise resolution](#promise-resolution)
- [Route Prefixing](#route-prefixing)
- [Handling of / route inside prefixed
plugins](#handling-of--route-inside-prefixed-plugins)
- [Custom Log Level](#custom-log-level)
- [Custom Log Serializer](#custom-log-serializer)
- [Config](#config)
- [Constraints](#constraints)
- [Version Constraints](#version-constraints)
- [Host Constraints](#host-constraints)
### Full declaration
<a id="full-declaration"></a>
```js
fastify.route(options)
```
### Routes options
<a id="options"></a>
* `method`: currently it supports `GET`, `HEAD`, `TRACE`, `DELETE`,
`OPTIONS`, `PATCH`, `PUT` and `POST`. To accept more methods,
the [`addHttpMethod`](./Server.md#addHttpMethod) must be used.
It could also be an array of methods.
* `url`: the path of the URL to match this route (alias: `path`).
* `schema`: an object containing the schemas for the request and response. They
need to be in [JSON Schema](https://json-schema.org/) format, check
[here](./Validation-and-Serialization.md) for more info.
* `body`: validates the body of the request if it is a POST, PUT, PATCH,
TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method.
* `querystring` or `query`: validates the querystring. This can be a complete
JSON Schema object, with the property `type` of `object` and `properties`
object of parameters, or simply the values of what would be contained in the
`properties` object as shown below.
* `params`: validates the params.
* `response`: filter and generate a schema for the response, setting a schema
allows us to have 10-20% more throughput.
* `exposeHeadRoute`: creates a sibling `HEAD` route for any `GET` routes.
Defaults to the value of [`exposeHeadRoutes`](./Server.md#exposeHeadRoutes)
instance option. If you want a custom `HEAD` handler without disabling this
option, make sure to define it before the `GET` route.
* `attachValidation`: attach `validationError` to request, if there is a schema
validation error, instead of sending the error to the error handler. The
default [error format](https://ajv.js.org/api.html#error-objects) is the Ajv
one.
* `onRequest(request, reply, done)`: a [function](./Hooks.md#onrequest) called
as soon as a request is received, it could also be an array of functions.
* `preParsing(request, reply, done)`: a [function](./Hooks.md#preparsing) called
before parsing the request, it could also be an array of functions.
* `preValidation(request, reply, done)`: a [function](./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.
* `preHandler(request, reply, done)`: a [function](./Hooks.md#prehandler) called
just before the request handler, it could also be an array of functions.
* `preSerialization(request, reply, payload, done)`: a
[function](./Hooks.md#preserialization) called just before the serialization,
it could also be an array of functions.
* `onSend(request, reply, payload, done)`: a [function](./Hooks.md#route-hooks)
called right before a response is sent, it could also be an array of
functions.
* `onResponse(request, reply, done)`: a [function](./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.
* `onTimeout(request, reply, done)`: a [function](./Hooks.md#ontimeout) called
when a request is timed out and the HTTP socket has been hung up.
* `onError(request, reply, error, done)`: a [function](./Hooks.md#onerror)
called when an Error is thrown or sent to the client by the route handler.
* `handler(request, reply)`: the function that will handle this request. The
[Fastify server](./Server.md) will be bound to `this` when the handler is
called. Note: using an arrow function will break the binding of `this`.
* `errorHandler(error, request, reply)`: a custom error handler for the scope of
the request. Overrides the default error global handler, and anything set by
[`setErrorHandler`](./Server.md#seterrorhandler), for requests to the route.
To access the default handler, you can access `instance.errorHandler`. Note
that this will point to fastify's default `errorHandler` only if a plugin
hasn't overridden it already.
* `childLoggerFactory(logger, binding, opts, rawReq)`: a custom factory function
that will be called to produce a child logger instance for every request.
See [`childLoggerFactory`](./Server.md#childloggerfactory) for more info.
Overrides the default logger factory, and anything set by
[`setChildLoggerFactory`](./Server.md#setchildloggerfactory), for requests to
the route. To access the default factory, you can access
`instance.childLoggerFactory`. Note that this will point to Fastify's default
`childLoggerFactory` only if a plugin hasn't overridden it already.
* `validatorCompiler({ schema, method, url, httpPart })`: function that builds
schemas for request validations. See the [Validation and
Serialization](./Validation-and-Serialization.md#schema-validator)
documentation.
* `serializerCompiler({ { schema, method, url, httpStatus, contentType } })`:
function that builds schemas for response serialization. See the [Validation and
Serialization](./Validation-and-Serialization.md#schema-serializer)
documentation.
* `schemaErrorFormatter(errors, dataVar)`: function that formats the errors from
the validation compiler. See the [Validation and
Serialization](./Validation-and-Serialization.md#error-handling)
documentation. Overrides the global schema error formatter handler, and
anything set by `setSchemaErrorFormatter`, for requests to the route.
* `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).
* `logLevel`: set log level for this route. See below.
* `logSerializers`: set serializers to log for this route.
* `config`: object used to store custom configuration.
* `version`: a [semver](https://semver.org/) compatible string that defined the
version of the endpoint. [Example](#version-constraints).
* `constraints`: defines route restrictions based on request properties or
values, enabling customized matching using
[find-my-way](https://github.com/delvedor/find-my-way) constraints. Includes
built-in `version` and `host` constraints, with support for custom constraint
strategies.
* `prefixTrailingSlash`: string used to determine how to handle passing `/` as a
route with a prefix.
* `both` (default): Will register both `/prefix` and `/prefix/`.
* `slash`: Will register only `/prefix/`.
* `no-slash`: Will register only `/prefix`.
Note: this option does not override `ignoreTrailingSlash` in
[Server](./Server.md) configuration.
* `request` is defined in [Request](./Request.md).
* `reply` is defined in [Reply](./Reply.md).
**Notice:** The documentation of `onRequest`, `preParsing`, `preValidation`,
`preHandler`, `preSerialization`, `onSend`, and `onResponse` are described in
more detail in [Hooks](./Hooks.md). Additionally, to send a response before the
request is handled by the `handler` please refer to [Respond to a request from a
hook](./Hooks.md#respond-to-a-request-from-a-hook).
Example:
```js
fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
```
### Shorthand declaration
<a id="shorthand-declaration"></a>
The above route declaration is more *Hapi*-like, but if you prefer an
*Express/Restify* approach, we support it as well:
`fastify.get(path, [options], handler)`
`fastify.head(path, [options], handler)`
`fastify.post(path, [options], handler)`
`fastify.put(path, [options], handler)`
`fastify.delete(path, [options], handler)`
`fastify.options(path, [options], handler)`
`fastify.patch(path, [options], handler)`
Example:
```js
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ hello: 'world' })
})
```
`fastify.all(path, [options], handler)` will add the same handler to all the
supported methods.
The handler may also be supplied via the `options` object:
```js
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)
```
> Note: if the handler is specified in both the `options` and as the third
> parameter to the shortcut method then throws a duplicate `handler` error.
### Url building
<a id="url-building"></a>
Fastify supports both static and dynamic URLs.
To register a **parametric** path, use the *colon* before the parameter name.
For **wildcard**, use the *star*. *Remember that static routes are always
checked before parametric and wildcard.*
```js
// parametric
fastify.get('/example/:userId', function (request, reply) {
// curl ${app-url}/example/12345
// userId === '12345'
const { userId } = request.params;
// your code here
})
fastify.get('/example/:userId/:secretToken', function (request, reply) {
// curl ${app-url}/example/12345/abc.zHi
// userId === '12345'
// secretToken === 'abc.zHi'
const { userId, secretToken } = request.params;
// your code here
})
// wildcard
fastify.get('/example/*', function (request, reply) {})
```
Regular expression routes are supported as well, but be aware that you have to
escape slashes. Take note that RegExp is also very expensive in terms of
performance!
```js
// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) {
// curl ${app-url}/example/12345.png
// file === '12345'
const { file } = request.params;
// your code here
})
```
It is possible to define more than one parameter within the same couple of slash
("/"). Such as:
```js
fastify.get('/example/near/:lat-:lng/radius/:r', function (request, reply) {
// curl ${app-url}/example/near/15°N-30°E/radius/20
// lat === "15°N"
// lng === "30°E"
// r ==="20"
const { lat, lng, r } = request.params;
// your code here
})
```
*Remember in this case to use the dash ("-") as parameters separator.*
Finally, it is possible to have multiple parameters with RegExp:
```js
fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, reply) {
// curl ${app-url}/example/at/08h24m
// hour === "08"
// minute === "24"
const { hour, minute } = request.params;
// your code here
})
```
In this case as parameter separator it is possible to use whatever character is
not matched by the regular expression.
The last parameter can be made optional if you add a question mark ("?") to the
end of the parameters name.
```js
fastify.get('/example/posts/:id?', function (request, reply) {
const { id } = request.params;
// your code here
})
```
In this case you can request `/example/posts` as well as `/example/posts/1`.
The optional param will be undefined if not specified.
Having a route with multiple parameters may negatively affect performance, so
prefer a single parameter approach whenever possible, especially on routes that
are on the hot path of your application. If you are interested in how we handle
the routing, check out [find-my-way](https://github.com/delvedor/find-my-way).
If you want a path containing a colon without declaring a parameter, use a
double colon. For example:
```js
fastify.post('/name::verb') // will be interpreted as /name:verb
```
### Async Await
<a id="async-await"></a>
Are you an `async/await` user? We have you covered!
```js
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return processed
})
```
As 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!
If you need it you can also send back the data to the user with `reply.send`. In
this case do not forget to `return reply` or `await reply` in your `async`
handler or you will introduce a race condition in certain situations.
```js
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return reply.send(processed)
})
```
If the route is wrapping a callback-based API that will call `reply.send()`
outside of the promise chain, it is possible to `await reply`:
```js
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})
```
Returning reply also works:
```js
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})
```
**Warning:**
* 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.
* Calling `reply.send()` outside of the promise is possible but requires special
attention. For more details read [promise-resolution](#promise-resolution).
* You cannot return `undefined`. For more details read
[promise-resolution](#promise-resolution).
### Promise resolution
<a id="promise-resolution"></a>
If your handler is an `async` function or returns a promise, you should be aware
of the special behavior that is necessary to support the callback and promise
control-flow. When the handler's promise is resolved, the reply will be
automatically sent with its value unless you explicitly await or return `reply`
in your handler.
1. If you want to use `async/await` or promises but respond with a value with
`reply.send`:
- **Do** `return reply` / `await reply`.
- **Do not** forget to call `reply.send`.
2. If you want to use `async/await` or promises:
- **Do not** use `reply.send`.
- **Do** return the value that you want to send.
In this way, we can support both `callback-style` and `async-await`, with the
minimum trade-off. Despite so much freedom we highly recommend going with only
one style because error handling should be handled in a consistent way within
your application.
**Notice**: Every async function returns a promise by itself.
### Route Prefixing
<a id="route-prefixing"></a>
Sometimes 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. Fastify offers you a fast and smart way to create
different versions of the same API without changing all the route names by hand,
*route prefixing*. Let's see how it works:
```js
// server.js
const fastify = require('fastify')()
fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
fastify.listen({ port: 3000 })
```
```js
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
```
```js
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}
```
Fastify 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!)*.
Now your clients will have access to the following routes:
- `/v1/user`
- `/v2/user`
You can do this as many times as you want, it also works for nested `register`,
and route parameters are supported as well.
In case you want to use prefix for all of your routes, you can put them inside a
plugin:
```js
const fastify = require('fastify')()
const route = {
method: 'POST',
url: '/login',
handler: () => {},
schema: {},
}
fastify.register(function (app, _, done) {
app.get('/users', () => {})
app.route(route)
done()
}, { prefix: '/v1' }) // global route prefix
await fastify.listen({ port: 3000 })
```
### Route Prefixing and fastify-plugin
<a id="fastify-plugin"></a>
Be aware that if you use
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) for wrapping your
routes, this option will not work. You can still make it work by wrapping a
plugin in a plugin, e. g.:
```js
const fp = require('fastify-plugin')
const routes = require('./lib/routes')
module.exports = fp(async function (app, opts) {
app.register(routes, {
prefix: '/v1',
})
}, {
name: 'my-routes'
})
```
#### Handling of / route inside prefixed plugins
The `/` route has different behavior depending on if the prefix ends with `/` or
not. As an example, if we consider a prefix `/something/`, adding a `/` route
will only match `/something/`. If we consider a prefix `/something`, adding a
`/` route will match both `/something` and `/something/`.
See the `prefixTrailingSlash` route option above to change this behavior.
### Custom Log Level
<a id="custom-log-level"></a>
You might need different log levels in your routes; Fastify achieves this in a
very straightforward way.
You 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#level-string)
that you need.
Be aware that if you set the `logLevel` at plugin level, also the
[`setNotFoundHandler`](./Server.md#setnotfoundhandler) and
[`setErrorHandler`](./Server.md#seterrorhandler) will be affected.
```js
// server.js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), { logLevel: 'warn' })
fastify.register(require('./routes/events'), { logLevel: 'debug' })
fastify.listen({ port: 3000 })
```
Or you can directly pass it to a route:
```js
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
```
*Remember that the custom log level is applied only to the routes, and not to
the global Fastify Logger, accessible with `fastify.log`*
### Custom Log Serializer
<a id="custom-log-serializer"></a>
In some contexts, 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 custom
[`serializers`](https://github.com/pinojs/pino/blob/master/docs/api.md#serializers-object)
and attach them in the right context!
```js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), {
logSerializers: {
user: (value) => `My serializer one - ${value.name}`
}
})
fastify.register(require('./routes/events'), {
logSerializers: {
user: (value) => `My serializer two - ${value.name} ${value.surname}`
}
})
fastify.listen({ port: 3000 })
```
You can inherit serializers by context:
```js
const fastify = Fastify({
logger: {
level: 'info',
serializers: {
user (req) {
return {
method: req.method,
url: req.url,
headers: req.headers,
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket.remotePort
}
}
}
}
})
fastify.register(context1, {
logSerializers: {
user: value => `My serializer father - ${value}`
}
})
async function context1 (fastify, opts) {
fastify.get('/', (req, reply) => {
req.log.info({ user: 'call father serializer', key: 'another key' })
// shows: { user: 'My serializer father - call father serializer', key: 'another key' }
reply.send({})
})
}
fastify.listen({ port: 3000 })
```
### Config
<a id="routes-config"></a>
Registering a new handler, you can pass a configuration object to it and
retrieve it in the handler.
```js
// server.js
const fastify = require('fastify')()
function handler (req, reply) {
reply.send(reply.routeOptions.config.output)
}
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
fastify.listen({ port: 3000 })
```
### Constraints
<a id="constraints"></a>
Fastify supports constraining routes to match only certain requests based on
some property of the request, like the `Host` header, or any other value via
[`find-my-way`](https://github.com/delvedor/find-my-way) constraints.
Constraints are specified in the `constraints` property of the route options.
Fastify has two built-in constraints ready for use: the `version` constraint and
the `host` constraint, and you can add your own custom constraint strategies to
inspect other parts of a request to decide if a route should be executed for a
request.
#### Version Constraints
You can provide a `version` key in the `constraints` option to a route.
Versioned routes allow you to declare multiple handlers for the same HTTP route
path, which will then be matched according to each request's `Accept-Version`
header. The `Accept-Version` header value should follow the
[semver](https://semver.org/) specification, and routes should be declared with
exact semver versions for matching.
Fastify will require a request `Accept-Version` header to be set if the route
has a version set, and will prefer a versioned route to a non-versioned route
for the same path. Advanced version ranges and pre-releases currently are not
supported.
*Be aware that using this feature will cause a degradation of the overall
performances of the router.*
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' },
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Accept-Version': '1.x' // it could also be '1.2.0' or '1.2.x'
}
}, (err, res) => {
// { hello: 'world' }
})
```
> ## ⚠ Security Notice
> Remember to set a
> [`Vary`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary)
> header in your responses with the value you are using for defining the
> versioning (e.g.: `'Accept-Version'`), to prevent cache poisoning attacks. You
> can also configure this as part of your Proxy/CDN.
>
> ```js
> const append = require('vary').append
> fastify.addHook('onSend', (req, reply, payload, done) => {
> if (req.headers['accept-version']) { // or the custom header you are using
> let value = reply.getHeader('Vary') || ''
> const header = Array.isArray(value) ? value.join(', ') : String(value)
> if ((value = append(header, 'Accept-Version'))) { // or the custom header you are using
> reply.header('Vary', value)
> }
> }
> done()
> })
> ```
If you declare multiple versions with the same major or minor, Fastify will
always choose the highest compatible with the `Accept-Version` header value.
If the request will not have the `Accept-Version` header, a 404 error will be
returned.
It is possible to define a custom version matching logic. This can be done
through the [`constraints`](./Server.md#constraints) configuration when creating
a Fastify server instance.
#### Host Constraints
You can provide a `host` key in the `constraints` route option for to limit that
route to only be matched for certain values of the request `Host` header. `host`
constraint values can be specified as strings for exact matches or RegExps for
arbitrary host matching.
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'auth.fastify.dev' },
handler: function (request, reply) {
reply.send('hello world from auth.fastify.dev')
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'example.com'
}
}, (err, res) => {
// 404 because the host doesn't match the constraint
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'auth.fastify.dev'
}
}, (err, res) => {
// => 'hello world from auth.fastify.dev'
})
```
RegExp `host` constraints can also be specified allowing constraining to hosts
matching wildcard subdomains (or any other pattern):
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.dev/ }, // will match any subdomain of fastify.dev
handler: function (request, reply) {
reply.send('hello world from ' + request.headers.host)
}
})
```
#### Asynchronous Custom Constraints
Custom constraints can be provided and the `constraint` criteria can be
fetched from another source such as `database`. The use of asynchronous
custom constraints should be a last resort as it impacts router
performance.
```js
function databaseOperation(field, done) {
done(null, field)
}
const secret = {
// strategy name for referencing in the route handler `constraints` options
name: 'secret',
// storage factory for storing routes in the find-my-way route tree
storage: function () {
let handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
// function to get the value of the constraint from each incoming request
deriveConstraint: (req, ctx, done) => {
databaseOperation(req.headers['secret'], done)
},
// optional flag marking if handlers without constraints can match requests that have a value for this constraint
mustMatchWhenDerived: true
}
```
> ## ⚠ Security Notice
> When using with asynchronous constraint. It is highly recommend never return error
> inside the callback. If the error is not preventable, it is recommended to provide
> a custom `frameworkErrors` handler to deal with it. Otherwise, you route selection
> may break or expose sensitive information to attackers.
>
> ```js
> const Fastify = require('fastify')
>
> const fastify = Fastify({
> frameworkErrors: function (err, res, res) {
> if (err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
> res.code(400)
> return res.send("Invalid header provided")
> } else {
> res.send(err)
> }
> }
> })
> ```
|