strong-tie/inbound-calls
0
1<h1 align="center">Fastify</h1>2 3## Getting Started4 5Hello! Thank you for checking out Fastify!6 7This document aims to be a gentle introduction to the framework and its8features. It is an elementary preface with examples and links to other parts of9the documentation.10 11Let's start!12 13### Install14<a id="install"></a>15 16Install with npm:17```sh18npm i fastify19```20 21Install with yarn:22```sh23yarn add fastify24```25 26### Your first server27<a id="first-server"></a>28 29Let's write our first server:30```js31// Require the framework and instantiate it32 33// ESM34import Fastify from 'fastify'35 36const fastify = Fastify({37 logger: true38})39// CommonJs40const fastify = require('fastify')({41 logger: true42})43 44// Declare a route45fastify.get('/', function (request, reply) {46 reply.send({ hello: 'world' })47})48 49// Run the server!50fastify.listen({ port: 3000 }, function (err, address) {51 if (err) {52 fastify.log.error(err)53 process.exit(1)54 }55 // Server is now listening on ${address}56})57```58 59> If you are using ECMAScript Modules (ESM) in your project, be sure to60> include "type": "module" in your package.json.61>```js62>{63> "type": "module"64>}65>```66 67Do you prefer to use `async/await`? Fastify supports it out-of-the-box.68 69```js70// ESM71import Fastify from 'fastify'72 73const fastify = Fastify({74 logger: true75})76// CommonJs77const fastify = require('fastify')({78 logger: true79})80 81fastify.get('/', async (request, reply) => {82 return { hello: 'world' }83})84 85/**86 * Run the server!87 */88const start = async () => {89 try {90 await fastify.listen({ port: 3000 })91 } catch (err) {92 fastify.log.error(err)93 process.exit(1)94 }95}96start()97```98 99Awesome, that was easy.100 101Unfortunately, writing a complex application requires significantly more code102than this example. A classic problem when you are building a new application is103how to handle multiple files, asynchronous bootstrapping, and the architecture104of your code.105 106Fastify offers an easy platform that helps to solve all of the problems outlined107above, and more!108 109> ## Note110> The above examples, and subsequent examples in this document, default to111> listening *only* on the localhost `127.0.0.1` interface. To listen on all112> available IPv4 interfaces the example should be modified to listen on113> `0.0.0.0` like so:114>115> ```js116> fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) {117> if (err) {118> fastify.log.error(err)119> process.exit(1)120> }121> fastify.log.info(`server listening on ${address}`)122> })123> ```124>125> Similarly, specify `::1` to accept only local connections via IPv6. Or specify126> `::` to accept connections on all IPv6 addresses, and, if the operating system127> supports it, also on all IPv4 addresses.128>129> When deploying to a Docker (or another type of) container using `0.0.0.0` or130> `::` would be the easiest method for exposing the application.131 132### Your first plugin133<a id="first-plugin"></a>134 135As with JavaScript, where everything is an object, with Fastify everything is a136plugin.137 138Before digging into it, let's see how it works!139 140Let's declare our basic server, but instead of declaring the route inside the141entry point, we'll declare it in an external file (check out the [route142declaration](../Reference/Routes.md) docs).143```js144// ESM145import Fastify from 'fastify'146import firstRoute from './our-first-route.js'147/**148 * @type {import('fastify').FastifyInstance} Instance of Fastify149 */150const fastify = Fastify({151 logger: true152})153 154fastify.register(firstRoute)155 156fastify.listen({ port: 3000 }, function (err, address) {157 if (err) {158 fastify.log.error(err)159 process.exit(1)160 }161 // Server is now listening on ${address}162})163```164 165```js166// CommonJs167/**168 * @type {import('fastify').FastifyInstance} Instance of Fastify169 */170const fastify = require('fastify')({171 logger: true172})173 174fastify.register(require('./our-first-route'))175 176fastify.listen({ port: 3000 }, function (err, address) {177 if (err) {178 fastify.log.error(err)179 process.exit(1)180 }181 // Server is now listening on ${address}182})183```184 185 186```js187// our-first-route.js188 189/**190 * Encapsulates the routes191 * @param {FastifyInstance} fastify Encapsulated Fastify Instance192 * @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options193 */194async function routes (fastify, options) {195 fastify.get('/', async (request, reply) => {196 return { hello: 'world' }197 })198}199 200//ESM201export default routes;202 203// CommonJs204module.exports = routes205```206In this example, we used the `register` API, which is the core of the Fastify207framework. It is the only way to add routes, plugins, et cetera.208 209At the beginning of this guide, we noted that Fastify provides a foundation that210assists with asynchronous bootstrapping of your application. Why is this211important?212 213Consider the scenario where a database connection is needed to handle data214storage. The database connection needs to be available before the server is215accepting connections. How do we address this problem?216 217A typical solution is to use a complex callback, or promises - a system that218will mix the framework API with other libraries and the application code.219 220Fastify handles this internally, with minimum effort!221 222Let's rewrite the above example with a database connection.223 224 225First, install `fastify-plugin` and `@fastify/mongodb`:226 227```sh228npm i fastify-plugin @fastify/mongodb229```230 231**server.js**232```js233// ESM234import Fastify from 'fastify'235import dbConnector from './our-db-connector.js'236import firstRoute from './our-first-route.js'237 238/**239 * @type {import('fastify').FastifyInstance} Instance of Fastify240 */241const fastify = Fastify({242 logger: true243})244fastify.register(dbConnector)245fastify.register(firstRoute)246 247fastify.listen({ port: 3000 }, function (err, address) {248 if (err) {249 fastify.log.error(err)250 process.exit(1)251 }252 // Server is now listening on ${address}253})254```255 256```js257// CommonJs258/**259 * @type {import('fastify').FastifyInstance} Instance of Fastify260 */261const fastify = require('fastify')({262 logger: true263})264 265fastify.register(require('./our-db-connector'))266fastify.register(require('./our-first-route'))267 268fastify.listen({ port: 3000 }, function (err, address) {269 if (err) {270 fastify.log.error(err)271 process.exit(1)272 }273 // Server is now listening on ${address}274})275 276```277 278**our-db-connector.js**279```js280// ESM281import fastifyPlugin from 'fastify-plugin'282import fastifyMongo from '@fastify/mongodb'283 284/**285 * @param {FastifyInstance} fastify286 * @param {Object} options287 */288async function dbConnector (fastify, options) {289 fastify.register(fastifyMongo, {290 url: 'mongodb://localhost:27017/test_database'291 })292}293 294// Wrapping a plugin function with fastify-plugin exposes the decorators295// and hooks, declared inside the plugin to the parent scope.296export default fastifyPlugin(dbConnector)297 298```299 300```js301// CommonJs302/**303 * @type {import('fastify-plugin').FastifyPlugin}304 */305const fastifyPlugin = require('fastify-plugin')306 307 308/**309 * Connects to a MongoDB database310 * @param {FastifyInstance} fastify Encapsulated Fastify Instance311 * @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options312 */313async function dbConnector (fastify, options) {314 fastify.register(require('@fastify/mongodb'), {315 url: 'mongodb://localhost:27017/test_database'316 })317}318 319// Wrapping a plugin function with fastify-plugin exposes the decorators320// and hooks, declared inside the plugin to the parent scope.321module.exports = fastifyPlugin(dbConnector)322 323```324 325**our-first-route.js**326```js327/**328 * A plugin that provide encapsulated routes329 * @param {FastifyInstance} fastify encapsulated fastify instance330 * @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options331 */332async function routes (fastify, options) {333 const collection = fastify.mongo.db.collection('test_collection')334 335 fastify.get('/', async (request, reply) => {336 return { hello: 'world' }337 })338 339 fastify.get('/animals', async (request, reply) => {340 const result = await collection.find().toArray()341 if (result.length === 0) {342 throw new Error('No documents found')343 }344 return result345 })346 347 fastify.get('/animals/:animal', async (request, reply) => {348 const result = await collection.findOne({ animal: request.params.animal })349 if (!result) {350 throw new Error('Invalid value')351 }352 return result353 })354 355 const animalBodyJsonSchema = {356 type: 'object',357 required: ['animal'],358 properties: {359 animal: { type: 'string' },360 },361 }362 363 const schema = {364 body: animalBodyJsonSchema,365 }366 367 fastify.post('/animals', { schema }, async (request, reply) => {368 // we can use the `request.body` object to get the data sent by the client369 const result = await collection.insertOne({ animal: request.body.animal })370 return result371 })372}373 374module.exports = routes375```376 377Wow, that was fast!378 379Let's recap what we have done here since we've introduced some new concepts.380 381As you can see, we used `register` for both the database connector and the382registration of the routes.383 384This is one of the best features of Fastify, it will load your plugins in the385same order you declare them, and it will load the next plugin only once the386current one has been loaded. In this way, we can register the database connector387in the first plugin and use it in the second *(read388[here](../Reference/Plugins.md#handle-the-scope) to understand how to handle the389scope of a plugin)*.390 391Plugin loading starts when you call `fastify.listen()`, `fastify.inject()` or392`fastify.ready()`393 394The MongoDB plugin uses the `decorate` API to add custom objects to the Fastify395instance, making them available for use everywhere. Use of this API is396encouraged to facilitate easy code reuse and to decrease code or logic397duplication.398 399To dig deeper into how Fastify plugins work, how to develop new plugins, and for400details on how to use the whole Fastify API to deal with the complexity of401asynchronously bootstrapping an application, read [the hitchhiker's guide to402plugins](./Plugins-Guide.md).403 404### Loading order of your plugins405<a id="plugin-loading-order"></a>406 407To guarantee consistent and predictable behavior of your application, we highly408recommend to always load your code as shown below:409```410└── plugins (from the Fastify ecosystem)411└── your plugins (your custom plugins)412└── decorators413└── hooks414└── your services415```416In this way, you will always have access to all of the properties declared in417the current scope.418 419As discussed previously, Fastify offers a solid encapsulation model, to help you420build your application as single and independent services. If you want to421register a plugin only for a subset of routes, you just have to replicate the422above structure.423```424└── plugins (from the Fastify ecosystem)425└── your plugins (your custom plugins)426└── decorators427└── hooks428└── your services429 │430 └── service A431 │ └── plugins (from the Fastify ecosystem)432 │ └── your plugins (your custom plugins)433 │ └── decorators434 │ └── hooks435 │ └── your services436 │437 └── service B438 └── plugins (from the Fastify ecosystem)439 └── your plugins (your custom plugins)440 └── decorators441 └── hooks442 └── your services443```444 445### Validate your data446<a id="validate-data"></a>447 448Data validation is extremely important and a core concept of the framework.449 450To validate incoming requests, Fastify uses [JSON451Schema](https://json-schema.org/).452 453Let's look at an example demonstrating validation for routes:454```js455/**456 * @type {import('fastify').RouteShorthandOptions}457 * @const458 */459const opts = {460 schema: {461 body: {462 type: 'object',463 properties: {464 someKey: { type: 'string' },465 someOtherKey: { type: 'number' }466 }467 }468 }469}470 471fastify.post('/', opts, async (request, reply) => {472 return { hello: 'world' }473})474```475This example shows how to pass an options object to the route, which accepts a476`schema` key that contains all of the schemas for route, `body`, `querystring`,477`params`, and `headers`.478 479Read [Validation and480Serialization](../Reference/Validation-and-Serialization.md) to learn more.481 482### Serialize your data483<a id="serialize-data"></a>484 485Fastify has first-class support for JSON. It is extremely optimized to parse486JSON bodies and serialize JSON output.487 488To speed up JSON serialization (yes, it is slow!) use the `response` key of the489schema option as shown in the following example:490```js491/**492 * @type {import('fastify').RouteShorthandOptions}493 * @const494 */495const opts = {496 schema: {497 response: {498 200: {499 type: 'object',500 properties: {501 hello: { type: 'string' }502 }503 }504 }505 }506}507 508fastify.get('/', opts, async (request, reply) => {509 return { hello: 'world' }510})511```512By specifying a schema as shown, you can speed up serialization by a factor of5132-3. This also helps to protect against leakage of potentially sensitive data,514since Fastify will serialize only the data present in the response schema. Read515[Validation and Serialization](../Reference/Validation-and-Serialization.md) to516learn more.517 518### Parsing request payloads519<a id="request-payload"></a>520 521Fastify parses `'application/json'` and `'text/plain'` request payloads522natively, with the result accessible from the [Fastify523request](../Reference/Request.md) object at `request.body`.524 525The following example returns the parsed body of a request back to the client:526 527```js528/**529 * @type {import('fastify').RouteShorthandOptions}530 */531const opts = {}532fastify.post('/', opts, async (request, reply) => {533 return request.body534})535```536 537Read [Content-Type Parser](../Reference/ContentTypeParser.md) to learn more538about Fastify's default parsing functionality and how to support other content539types.540 541### Extend your server542<a id="extend-server"></a>543 544Fastify is built to be extremely extensible and minimal, we believe that a545bare-bones framework is all that is necessary to make great applications546possible.547 548In other words, Fastify is not a "batteries included" framework, and relies on549an amazing [ecosystem](./Ecosystem.md)!550 551### Test your server552<a id="test-server"></a>553 554Fastify does not offer a testing framework, but we do recommend a way to write555your tests that use the features and architecture of Fastify.556 557Read the [testing](./Testing.md) documentation to learn more!558 559### Run your server from CLI560<a id="cli"></a>561 562Fastify also has CLI integration thanks to563[fastify-cli](https://github.com/fastify/fastify-cli).564 565First, install `fastify-cli`:566 567```sh568npm i fastify-cli569```570 571You can also install it globally with `-g`.572 573Then, add the following lines to `package.json`:574```json575{576 "scripts": {577 "start": "fastify start server.js"578 }579}580```581 582And create your server file(s):583```js584// server.js585'use strict'586 587module.exports = async function (fastify, opts) {588 fastify.get('/', async (request, reply) => {589 return { hello: 'world' }590 })591}592```593 594Then run your server with:595```bash596npm start597```598 599### Slides and Videos600<a id="slides"></a>601 602- Slides603 - [Take your HTTP server to ludicrous604 speed](https://mcollina.github.io/take-your-http-server-to-ludicrous-speed)605 by [@mcollina](https://github.com/mcollina)606 - [What if I told you that HTTP can be607 fast](https://delvedor.github.io/What-if-I-told-you-that-HTTP-can-be-fast)608 by [@delvedor](https://github.com/delvedor)609 610- Videos611 - [Take your HTTP server to ludicrous612 speed](https://www.youtube.com/watch?v=5z46jJZNe8k) by613 [@mcollina](https://github.com/mcollina)614 - [What if I told you that HTTP can be615 fast](https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/)616 by [@delvedor](https://github.com/delvedor)617 