Routes
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
- Routes options
- Shorthand declaration
- Url building
- Async Await
- Promise resolution
- Route Prefixing
- Custom Log Level
- Custom Log Serializer
- Config
- Constraints
Full declaration
fastify.route(options)
Routes options
-
method: currently it supports'DELETE','GET','HEAD','PATCH','POST','PUT','OPTIONS','SEARCH','TRACE','PROPFIND','PROPPATCH','MKCOL','COPY','MOVE','LOCK','UNLOCK','REPORT'and'MKCALENDAR'. 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, COPY, MOVE, MKCOL, REPORT, MKCALENDAR or LOCK method.querystringorquery: validates the querystring. This can be a complete JSON Schema object, with the propertytypeofobjectandpropertiesobject of parameters, or simply the values of what would be contained in thepropertiesobject 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 siblingHEADroute for anyGETroutes. Defaults to the value ofexposeHeadRoutesinstance option. If you want a customHEADhandler without disabling this option, make sure to define it before theGETroute. -
attachValidation: attachvalidationErrorto 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 sharedpreValidationhooks, 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 tothiswhen the handler is called. Note: using an arrow function will break the binding ofthis. -
errorHandler(error, request, reply): a custom error handler for the scope of the request. Overrides the default error global handler, and anything set bysetErrorHandler, for requests to the route. To access the default handler, you can accessinstance.errorHandler. Note that this will point to fastify's defaulterrorHandleronly 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. SeechildLoggerFactoryfor more info. Overrides the default logger factory, and anything set bysetChildLoggerFactory, for requests to the route. To access the default factory, you can accessinstance.childLoggerFactory. Note that this will point to Fastify's defaultchildLoggerFactoryonly 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 bysetSchemaErrorFormatter, 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 withfastify(options). Defaults to1048576(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-inversionandhostconstraints, 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/prefixand/prefix/.slash: Will register only/prefix/.no-slash: Will register only/prefix.
Note: this option does not override
ignoreTrailingSlashin Server configuration. -
requestis defined in Request. -
replyis defined in Reply.
Notice: The documentation of onRequest, preParsing, preValidation,
preHandler, preSerialization, onSend, and onResponse are described in
more detail in Hooks. Additionally, to send a response before the
request is handled by the handler please refer to Respond to a request from a
hook.
Example:
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
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: if the handler is specified in both the
optionsand as the third parameter to the shortcut method then throws a duplicatehandlererror.
Url building
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.
// 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!
// 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 if you add a question mark ("?") to the end of the parameters name.
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.
If you want a path containing a colon without declaring a parameter, use a double colon. For example:
fastify.post('/name::verb') // will be interpreted as /name:verb
Async Await
Are you an async/await user? We have you covered!
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. 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.
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var 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 valueandreply.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. - You cannot return
undefined. For more details read promise-resolution.
Promise resolution
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.
- If you want to use
async/awaitor promises but respond with a value withreply.send:- Do
return reply/await reply. - Do not forget to call
reply.send.
- Do
- If you want to use
async/awaitor promises:- Do not use
reply.send. - Do return the value that you want to send.
- Do not use
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
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:
// 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 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:
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: 0 })
Route Prefixing and fastify-plugin
Be aware that if you use
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.:
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.