The route methods configure the endpoints of the application. Routes can be declared using the shorthand method or the full declaration.
fastify.route(options)
method
: currently it supports GET
, HEAD
, TRACE
, DELETE
,
OPTIONS
, PATCH
, PUT
and POST
. To accept more methods,
the 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 format, check
here 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
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 is the Ajv
one.
onRequest(request, reply, done)
: a function called
as soon as a request is received, it could also be an array of functions.
preParsing(request, reply, done)
: a function called
before parsing the request, it could also be an array of functions.
preValidation(request, reply, done)
: a function
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 called
just before the request handler, it could also be an array of functions.
preSerialization(request, reply, payload, done)
: a
function called just before the serialization,
it could also be an array of functions.
onSend(request, reply, payload, done)
: a function
called right before a response is sent, it could also be an array of
functions.
onResponse(request, reply, done)
: a function 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 called
when a request is timed out and the HTTP socket has been hung up.
onError(request, reply, error, done)
: a function
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 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
, 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
for more info.
Overrides the default logger factory, and anything set by
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
documentation.
serializerCompiler({ { schema, method, url, httpStatus, contentType } })
:
function that builds schemas for response serialization. See the Validation and
Serialization
documentation.
schemaErrorFormatter(errors, dataVar)
: function that formats the errors from
the validation compiler. See the Validation and
Serialization
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 compatible string that defined the
version of the endpoint. Example.
constraints
: defines route restrictions based on request properties or
values, enabling customized matching using
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 configuration.
request
is defined in Request.
reply
is defined in Reply.
🛈 Note: The documentation for
onRequest
,preParsing
,preValidation
,preHandler
,preSerialization
,onSend
, andonResponse
is detailed in Hooks. To send a response before the request is handled by thehandler
, see Respond to a request from a hook.
Example:
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' })
}
})
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:
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:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)
🛈 Note: Specifying the handler in both
options
and as the third parameter to the shortcut method throws a duplicatehandler
error.
Fastify supports both static and dynamic URLs.
To register a parametric path, use a colon before the parameter name. For wildcard, use a star. Static routes are always checked before parametric and wildcard routes.
// 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, but slashes must be escaped. Take note that RegExp is also very expensive in terms of performance!
// 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:
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:
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 by adding a question mark ("?") to the end of the parameter name.
fastify.get('/example/posts/:id?', function (request, reply) {
const { id } = request.params;
// your code here
})
In this case, /example/posts
and /example/posts/1
are both valid. The
optional param will be undefined
if not specified.
Having a route with multiple parameters may negatively affect performance. Prefer a single parameter approach, especially on routes that are on the hot path of your application. For more details, see find-my-way.
To include a colon in a path without declaring a parameter, use a double colon. For example:
fastify.post('/name::verb') // will be interpreted as /name:verb
Are you an async/await
user? We have you covered!
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return processed
})
As shown, reply.send
is not called to send data back to the user. Simply
return the body and you are done!
If needed, you can also send data back with reply.send
. In this case, do not
forget to return reply
or await reply
in your async
handler to avoid race
conditions.
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
:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})
Returning reply also works:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})
⚠ Warning:
- When using both
return value
andreply.send(value)
, the first one takes precedence, the second is discarded, and a warn log is emitted.- Calling
reply.send()
outside of the promise is possible but requires special attention. See promise-resolution.undefined
cannot be returned. See promise-resolution.
If the handler is an async
function or returns a promise, be aware of the
special behavior to support callback and promise control-flow. When the
handler's promise resolves, the reply is automatically sent with its value
unless you explicitly await or return reply
in the handler.
async/await
or promises but responding with reply.send
:
return reply
/ await reply
.reply.send
.async/await
or promises:
reply.send
.This approach supports both callback-style
and async-await
with minimal
trade-off. However, it is recommended to use only one style for consistent
error handling within your application.
🛈 Note: Every async function returns a promise by itself.
Sometimes maintaining multiple versions of the same API is necessary. A common
approach is to prefix routes with the API version number, e.g., /v1/user
.
Fastify offers a fast and smart way to create different versions of the same API
without changing all the route names by hand, called route prefixing. Here is
how it works:
// 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 })
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}
Fastify will not complain about using the same name for two different routes because it handles the prefix automatically at compilation time. This ensures performance is not affected.
Now clients will have access to the following routes:
/v1/user
/v2/user
This can be done multiple times and works for nested register
. Route
parameters are also supported.
To use a prefix for all routes, place them inside a plugin:
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 })
If using fastify-plugin
to wrap
routes, this option will not work. To make it work, wrap a plugin in a plugin:
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'
})
The /
route behaves differently based on whether the prefix ends with /
.
For example, with a prefix /something/
, adding a /
route matches only
/something/
. With a prefix /something
, adding a /
route matches both
/something
and /something/
.
See the prefixTrailingSlash
route option above to change this behavior.
Different log levels can be set for routes in Fastify by passing the logLevel
option to the plugin or route with the desired
value.
Be aware that setting logLevel
at the plugin level also affects
setNotFoundHandler
and
setErrorHandler
.
// 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 pass it directly to a route:
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
Remember that the custom log level applies only to routes, not to the global
Fastify Logger, accessible with fastify.log
.
In some contexts, logging a large object may waste resources. Define custom
serializers
and attach them in the appropriate context.
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 })
Serializers can be inherited by context:
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 })
Registering a new handler, you can pass a configuration object to it and retrieve it in the handler.
// 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 })
Fastify supports constraining routes to match certain requests based on
properties like the Host
header or any other value via
find-my-way
constraints.
Constraints are specified in the constraints
property of the route options.
Fastify has two built-in constraints: version
and host
. Custom constraint
strategies can be added to inspect other parts of a request to decide if a route
should be executed.
You can provide a version
key in the constraints
option to a route.
Versioned routes allows multiple handlers to be declared for the same HTTP
route path, matched according to the request's Accept-Version
header.
The Accept-Version
header value should follow the
semver 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.
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' }
})
⚠ Warning: Set a
Vary
header in responses with the value used for versioning (e.g.,'Accept-Version'
) to prevent cache poisoning attacks. This can also be configured in a Proxy/CDN.const append = require('vary').append fastify.addHook('onSend', (req, reply, payload, done) => { if (req.headers['accept-version']) { // or the custom header being used let value = reply.getHeader('Vary') || '' const header = Array.isArray(value) ? value.join(', ') : String(value) if ((value = append(header, 'Accept-Version'))) { // or the custom header being used reply.header('Vary', value) } } done() })
If multiple versions with the same major or minor are declared, Fastify will
always choose the highest compatible with the Accept-Version
header value.
If the request lacks an Accept-Version
header, a 404 error will be returned.
Custom version matching logic can be defined through the
constraints
configuration when creating a Fastify
server instance.
Provide a host
key in the constraints
route option to limit the route to
certain values of the request Host
header. host
constraint values can be
specified as strings for exact matches or RegExps for arbitrary host matching.
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):
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)
}
})
Custom constraints can be provided, and the constraint
criteria can be
fetched from another source such as a database. Use asynchronous custom
constraints as a last resort, as they impact router performance.
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
}
⚠ Warning: When using asynchronous constraints, avoid returning errors inside the callback. If errors are unavoidable, provide a custom
frameworkErrors
handler to manage them. Otherwise, route selection may break or expose sensitive information.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) } } })
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )