|
|
<h1 align="center">Fastify</h1>
|
|
|
|
|
|
## Routes
|
|
|
|
|
|
The routes 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 `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`,
|
|
|
`'POST'`, `'PUT'` and `'OPTIONS'`. 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, or PATCH
|
|
|
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.
|
|
|
* `onRequest(request, reply, done)`: a [function](./Hooks.md#onrequest) as soon
|
|
|
that 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 hanged up.
|
|
|
* `onError(request, reply, error, done)`: a [function](./Hooks.md#onerror)
|
|
|
called when an Error is thrown or send 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.
|
|
|
* `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 } })`: 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).
|
|
|
* `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`.
|
|
|
|
|
|
`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: {
|
|
|
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 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', (request, reply) => {})
|
|
|
fastify.get('/example/:userId/:secretToken', (request, reply) => {})
|
|
|
|
|
|
// wildcard
|
|
|
fastify.get('/example/*', (request, reply) => {})
|
|
|
```
|
|
|
|
|
|
Regular expression routes are supported as well, but pay attention, RegExp are
|
|
|
very expensive in term of performance!
|
|
|
```js
|
|
|
// parametric with regexp
|
|
|
fastify.get('/example/:file(^\\d+).png', (request, reply) => {})
|
|
|
```
|
|
|
|
|
|
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', (request, reply) => {})
|
|
|
```
|
|
|
*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', (request, reply) => {})
|
|
|
```
|
|
|
In this case as parameter separator it is possible to use whatever character is
|
|
|
not matched by the regular expression.
|
|
|
|
|
|
Having a route with multiple parameters may affect negatively the performance,
|
|
|
so prefer 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) {
|
|
|
var data = await getData()
|
|
|
var 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`.
|
|
|
```js
|
|
|
fastify.get('/', options, async function (request, reply) {
|
|
|
var data = await getData()
|
|
|
var processed = await processData(data)
|
|
|
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.
|
|
|
* 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 a special behavior that 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.
|
|
|
|
|
|
1. If you want to use `async/await` or promises but return a value with
|
|
|
`reply.send`:
|
|
|
- **Do not** `return` any value.
|
|
|
- **Do not** forget to call `reply.send`.
|
|
|
2. If you want to use `async/await` or promises:
|
|
|
- **Do not** use `reply.send`.
|
|
|
- **Do not** return `undefined`.
|
|
|
|
|
|
In 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.
|
|
|
|
|
|
**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(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 works also for nested `register`
|
|
|
and routes parameter are supported as well. Be aware that if you use
|
|
|
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) this option will
|
|
|
not work.
|
|
|
|
|
|
#### Handling of / route inside prefixed plugins
|
|
|
|
|
|
The `/` route has a 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>
|
|
|
|
|
|
It could happen that you 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(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 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#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(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,
|
|
|
hostname: req.hostname,
|
|
|
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(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.context.config.output)
|
|
|
}
|
|
|
|
|
|
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
|
|
|
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
|
|
|
|
|
|
fastify.listen(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](http://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', async (req, reply) => {
|
|
|
> 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)
|
|
|
> }
|
|
|
> }
|
|
|
> })
|
|
|
> ```
|
|
|
|
|
|
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.io' },
|
|
|
handler: function (request, reply) {
|
|
|
reply.send('hello world from auth.fastify.io')
|
|
|
}
|
|
|
})
|
|
|
|
|
|
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.io'
|
|
|
}
|
|
|
}, (err, res) => {
|
|
|
// => 'hello world from auth.fastify.io'
|
|
|
})
|
|
|
```
|
|
|
|
|
|
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\.io/ }, // will match any subdomain of fastify.io
|
|
|
handler: function (request, reply) {
|
|
|
reply.send('hello world from ' + request.headers.host)
|
|
|
}
|
|
|
})
|
|
|
```
|