Getting Started
Install
1 | yarn add fastify |
First Server
1 | // Require the framework and instantiate it |
Schema Serialization
1 | const fastify = require('fastify')() |
Register
Register routes in seperate files
1 | // server.js |
1 | // route.js |
or async/await
:
1 | module.exports = async (fastify, options) => { |
Server Methods
server
fastify.server
: The Node Core server
object.
ready
Function called when all the plugins has been loaded. It takes an error parameter if something went wrong.
1 | fastify.ready(err => { if (err) throw err }) |
listen
Starts the server on the given port after all the plugins loaded, internally waits for the .ready()
event. The callback is the same as the Node Core.
1 | fastify.listen(3000, err => { |
Specifying an address is also supported:
1 | fastify.listen(3000, '127.0.0.1', err => { |
route
Method to add routes to the server, it also have shorthands.
routes iterator
The fastify instance is an Iterable object with all the registered routes. The route properties are the same the developer has declared.
close
fastify.close(callback)
, call this function to close the server instance and run the onClose
hook.
decorate*
Function useful if you need to decorate the fastify instance, Reply or Request.
register
Fastify allows the user to extends its functionalities with plugins. A plugin can be a set of routes, a server decorator or whatever.
use
Function to add middlewares to Fastify.
addHook
Function to add a specific hook in the lifecycle of Fastify.
logger
The logger instance.
Inject
Fake http injection(for testing purpose).
setSchemaCompiler
Set the schema compiler for all routes.
setNotFoundHandler
fastify.setNotFoundHandler(handler(req, reply))
: set the 404 handler. This call is fully encapsulated, so different plugins can set different not found handlers.
setErrorHandler
fastify.setErrorHandler(handler(err, reply))
: set a function that will be called whenever an error happens. The handler is fully encapsulated, so different plugins can set different error handlers.
Routes
1 | fastify.route(options) |
method
: currently it supportsDELETE
,GET
,HEAD
,PATCH
,POST
,PUT
, andOPTIONS
, it could 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 inJSON Schema
format.body
: validates the body of the request if it is a POST or PUT.querystring
: validates the querystring. This can be a complete JSON Schema Object, with the propertytype
ofobject
andproperties
object of parameters, or simply the values of what would be contained in the properties object as shown below.params
: validates the params.response
: fitler and generate a schema for the response, setting a schema allows us to have 10-20% more throughput.
beforeHandler(req, res, done)
: a function called just before the request handler, useful if you need to perform authentication at route level for example, it could also be and array of functions.handler(req, reply)
: the function that will handle this request.request
if defined inRequest
.reply
is defined inReply
.
Exmaple
1 | fastify.route({ |
ShortHands
fastify.method(path, [options], handler)
1 | const opts = { |
fastify.all(path, [options], handler)
will add the same handler to all the supported methods.
Async/Await
1 | fastify.get('/', options, async (req, reply) => { |
Route Prefixing
1 | // server.js |
Now your client can access to /v1/user
.
Be aware that if you use fastify-plugin
this option won’t work.
Logging
Logging is disabled by default, and you can enable it by passing { logger: true }
or { logger: { level: 'info' } }
when you create the fastify instance.
1 | const fastify = require('fastify')({ |
Middlewares
Fastify provides out of the box an asynchronous middleare engine compatible with Express and Restify middlewares.
Fastify middleware don’t support the full syntax middleware(err, req, res, next)
because error handling is done inside Fastify.
Also if you are using a middleware taht bundles different, smaller middlewares such as helmet, we recommand to use the single module to get better performances.
1 | fastify.use(require('cors')()) |
If you need to run a middleware only under certain path, just pass the path as first parameter to use
and you are done.
Note that this does not support routes with parameters, (eg: /users/:id/comments
) and wildcard is not supported in multiple paths.
1 | const serveStatic = require('serve-static') |
Hook
By using the hook you can interact directly inside the lifecycle of Fastify, there are three different Hooks that you can use(in order of execution):
onRequest
preHandler
onResponse
onClose
Example:
1 | fastify.addHook('onRequest', (req, res, next) => { |
If you want to pass a custom error code to the user, just use the reply.code()
:
1 | fastify.addHook('onRequest', (req, res, next) => { |
The error will be handled by Reply
The unique hook that is not inside the lifecycle is 'onClose'
, this one is triggered when you call fastify.close()
to stop the server, and it is useful if you have some plugins that need a ‘shutdown’ part, such as a connection to database.
Only for this hook the parameters of the functions changes.
1 | fastify.addHook('onClose', (instance, done) => { |
Scope
Except for onClose
all the hooks are encapsulated this means that you can decide where your hooks should run by using register
.
beforeHandler
beforeHandler
is not a standard hook like preHandler
, but is a function that your register right in the route option that will be executed only in the specified route.
beforeHandler
is executed always after the preHandler
hook.
1 | fastify.addHook('preHander', (req, reply, done) => { |
Decorators
If you need to add functionalities to the Fastify instance, the decorator
api is what you need.
This api allows you to add new properties to the Fastify instance, a property value is not restricted to be a function, could also be an object or a string for example.
Usage
decorate
Just call the decorate
api and pass the name of the new property and its value.
1 | fastify.decorate('utility', () => { |
As said above, you can decorate the instance with other values and not only functions:
1 | fastify.decorate('conf', { |
Once you decorate the instance you can access the value every time you need by using the name you passed as parameter:
1 | fastify.utility() |
Decorators are not overwritable, if you try to declare a decorator already declared, decorate
will throw an exception.
decorateReply
As the name suggest, this api is needed if you want to add new methods to the Reply
core object. Just call the decorateReply
api and pass the name of the new property and its value.
1 | fastify.decorateReply('utility', function () { |
Note: using an arrow function will break the binding of this
to the Fastify reply
instance.
decorateRequest
1 | fastify.decorateRequest('utility', function () { |
Note: using an arrow function will break the binding of this
to the Fastify request
instance.
extendServerError
If you need to extend the standard server error
, this api is what you need.
You must pass a function that returns an Object, Fastify will extend the server error with the returned object of your function. The function will receive the original error object.
1 | fastify.extendServerError(err => { |
Sync and Async
decorate
is synchronous API, if you need to add a decorator that has an asynchronous bootstrap, could happen that Fastify boots up before your decorator is ready. To avoid this issue you must use register
api in combination with fastify-plugin
.
Dependencies
If your decorator depends on another decorator, you can declare the dependencies of your function, it’s pretty easy, you just need to add an array of strings(representing the names of the decorators your are depending on) as third parameter.
1 | fastify.decorate('utility', fn, ['greet', 'log']) |
If a dependency is not satisfied, decorate
will throw an exception.
hasDecorator
1 | fastify.hasDecorator('utility') |
Validation and Serialize
Fastify uses a schema based approach and even if it is not mandatory we recommend to use JSON Schema to validate you routes and serialize your output, internally Fastify compiles the schema in a highly performance function.
Validation
The route validation internally uses Ajv
, which is highly performant JSON validator. Validate the input is very easy, just add the fields that you need inside the route schema and you are done.
The supported validations are
body: Validates the body of the request if it is a POST or PUT
querystring: 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 teh route params.
headers: Validates teh request headers.
Example:
1 | const schema = { |
Schema Compiler
The schemaCompiler
is a function that returns a function that validates the body, url parameters, headers and query string.
The default schemaCompiler
returns a function that implements the ajv
validation interface. fastify
use it internally to speed the valiation up.
Serialize
Usually you will send your data to the clients via JSON, and Fastify has a powerful tools to help you: fast-json-stringify
, which is used if you have provided an output schema in the route options.
1 | const schema = { |
As you can see the response schema is based on the status code, if you want to use the same schema for mutliple status code, you can use 2xx
.
1 | const schema = { |
Lifecycle
1 | Incoming Request |
Reply
The second parameter of the handler function is Reply
.
Reply is a core Fastify object that exposes the following functions:
.code(statusCode)
.header(name, value)
.type(value): Set the header
Content-Type
.redirect([code, ] url): default code 302
.serializer(function): Set a custom serializer for the payload
.send(payload)
.sent: A boolean value that you can use if you need to know it
send
has already been called.
1 | fastify.get('/', options, (request, reply) => { |
Code
If not set via reply.code
, the resulting statusCode
will be 200
Header
Sets a custom header to the response.
If you not set a Content-Type
header, Fastify assumes that you are using application/json
, unless you are send a stream, in that case Fastify recognize it and sets the Content-Type
at application/octet-stream
.
Redirect
Redirects a request to the specified url, the status code is optional, default to 302
.
1 | reply.redirect('/home') |
Type
Sets the content type for the response.
It’s the shortcut for reply.header('Content-Type', 'application/json')
Serializer
Send
Objects
If you are sending JSON Objects, send will serialize the object with fast-json-stringify
if you setted an output schema, otherwise fast-safe-stringify
Promises
Send handle natively the promsies and supports out of the box async-await:
1 | fastify.get('/promises', options, (req, reply) => { |
Streams
Send can also handle streams out of box, internally uses pump
to avoid leaks of file description. If you are sneding a stream and you have not setted a Content-Type
header, send will set it at application/octet-stream
.
Errors
If you pass to send an object that is an instance of Error, Fastify will automatically create error structured as the following:
1 | { |
Request
The first parameter of the handler function is Request
.
Request is a core Fastify object containing the following fields:
query
body
params: the params matching the URL
headers
req: the incoming HTTP request from Node core
log
1 | fastify.post('/:params', options, (req, reply) => { |
Content Type Parser
Natively Fastify supports only application/json
content-type. If you need to support different content types you can use the addContentTypeParser
api.
Catch All
1 | fastify.addContentTypeParser('*', function (req, done) { |
Plugins
Fastify allows the user to extend its functionalities with plugins. A plugin can be a set of routes, a server decorator or whatever.
The API you will need for one or more plugins is register
.
By default, register
creates a new scope, this means that if you do some changes to the Fastify instance(via decorate), this change will not be reflected to the current context ancestors, but only to its sons.
1 | fastify.register(plugin, [options]) |
Craete a Plugin
Create a plugin is very easy, you just need to create a function that takes three paremters, the fastify
instance, an options object and the next callback.
1 | module.exports = (fastify, opts, next) => { |
Handle the Scope
You have two ways to tell Fastify to avoid the creation of a new context:
Use the
fastify-plugin
moduleUse the
skip-override
hidden property
We recomended to use the fastify-plugin
module, because it solves this problem for you, and you can pass as parameter a version range of Fastify taht your plguin support.
1 | const fp = require('fastify-plguin') |