You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					557 lines
				
				14 KiB
			
		
		
			
		
	
	
					557 lines
				
				14 KiB
			| 
											3 years ago
										 | <h1 align="center">Fastify</h1> | ||
|  | 
 | ||
|  | ## Getting Started
 | ||
|  | 
 | ||
|  | Hello! Thank you for checking out Fastify! | ||
|  | 
 | ||
|  | This document aims to be a gentle introduction to the framework and its | ||
|  | features. It is an elementary preface with examples and links to other parts of | ||
|  | the documentation. | ||
|  | 
 | ||
|  | Let's start! | ||
|  | 
 | ||
|  | ### Install
 | ||
|  | <a id="install"></a> | ||
|  | 
 | ||
|  | Install with npm: | ||
|  | ``` | ||
|  | npm i fastify --save | ||
|  | ``` | ||
|  | Install with yarn: | ||
|  | ``` | ||
|  | yarn add fastify | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Your first server
 | ||
|  | <a id="first-server"></a> | ||
|  | 
 | ||
|  | Let's write our first server: | ||
|  | ```js | ||
|  | // Require the framework and instantiate it | ||
|  | 
 | ||
|  | // ESM | ||
|  | import Fastify from 'fastify' | ||
|  | const fastify = Fastify({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | // CommonJs | ||
|  | const fastify = require('fastify')({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | 
 | ||
|  | // Declare a route | ||
|  | fastify.get('/', function (request, reply) { | ||
|  |   reply.send({ hello: 'world' }) | ||
|  | }) | ||
|  | 
 | ||
|  | // Run the server! | ||
|  | fastify.listen(3000, function (err, address) { | ||
|  |   if (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  |   // Server is now listening on ${address} | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | Do you prefer to use `async/await`? Fastify supports it out-of-the-box. | ||
|  | 
 | ||
|  | *(We also suggest using | ||
|  | [make-promises-safe](https://github.com/mcollina/make-promises-safe) to avoid | ||
|  | file descriptor and memory leaks.)* | ||
|  | ```js | ||
|  | // ESM | ||
|  | import Fastify from 'fastify' | ||
|  | const fastify = Fastify({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | // CommonJs | ||
|  | const fastify = require('fastify')({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | 
 | ||
|  | fastify.get('/', async (request, reply) => { | ||
|  |   return { hello: 'world' } | ||
|  | }) | ||
|  | 
 | ||
|  | const start = async () => { | ||
|  |   try { | ||
|  |     await fastify.listen(3000) | ||
|  |   } catch (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  | } | ||
|  | start() | ||
|  | ``` | ||
|  | 
 | ||
|  | Awesome, that was easy. | ||
|  | 
 | ||
|  | Unfortunately, writing a complex application requires significantly more code | ||
|  | than this example. A classic problem when you are building a new application is | ||
|  | how to handle multiple files, asynchronous bootstrapping, and the architecture | ||
|  | of your code. | ||
|  | 
 | ||
|  | Fastify offers an easy platform that helps to solve all of the problems outlined | ||
|  | above, and more! | ||
|  | 
 | ||
|  | > ## Note
 | ||
|  | > The above examples, and subsequent examples in this document, default to
 | ||
|  | > listening *only* on the localhost `127.0.0.1` interface. To listen on all
 | ||
|  | > available IPv4 interfaces the example should be modified to listen on
 | ||
|  | > `0.0.0.0` like so:
 | ||
|  | > | ||
|  | > ```js
 | ||
|  | > fastify.listen(3000, '0.0.0.0', function (err, address) {
 | ||
|  | >   if (err) {
 | ||
|  | >     fastify.log.error(err)
 | ||
|  | >     process.exit(1)
 | ||
|  | >   }
 | ||
|  | >   fastify.log.info(`server listening on ${address}`)
 | ||
|  | > })
 | ||
|  | > ```
 | ||
|  | > | ||
|  | > Similarly, specify `::1` to accept only local connections via IPv6. Or specify
 | ||
|  | > `::` to accept connections on all IPv6 addresses, and, if the operating system
 | ||
|  | > supports it, also on all IPv4 addresses.
 | ||
|  | > | ||
|  | > When deploying to a Docker (or another type of) container using `0.0.0.0` or
 | ||
|  | > `::` would be the easiest method for exposing the application.
 | ||
|  | 
 | ||
|  | ### Your first plugin
 | ||
|  | <a id="first-plugin"></a> | ||
|  | 
 | ||
|  | As with JavaScript, where everything is an object, with Fastify everything is a | ||
|  | plugin. | ||
|  | 
 | ||
|  | Before digging into it, let's see how it works! | ||
|  | 
 | ||
|  | Let's declare our basic server, but instead of declaring the route inside the | ||
|  | entry point, we'll declare it in an external file (check out the [route | ||
|  | declaration](../Reference/Routes.md) docs). | ||
|  | ```js | ||
|  | // ESM | ||
|  | import Fastify from 'fastify' | ||
|  | import firstRoute from './our-first-route' | ||
|  | const fastify = Fastify({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | 
 | ||
|  | fastify.register(firstRoute) | ||
|  | 
 | ||
|  | fastify.listen(3000, function (err, address) { | ||
|  |   if (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  |   // Server is now listening on ${address} | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ```js | ||
|  | // CommonJs | ||
|  | const fastify = require('fastify')({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | 
 | ||
|  | fastify.register(require('./our-first-route')) | ||
|  | 
 | ||
|  | fastify.listen(3000, function (err, address) { | ||
|  |   if (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  |   // Server is now listening on ${address} | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ```js | ||
|  | // our-first-route.js | ||
|  | 
 | ||
|  | async function routes (fastify, options) { | ||
|  |   fastify.get('/', async (request, reply) => { | ||
|  |     return { hello: 'world' } | ||
|  |   }) | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = routes | ||
|  | ``` | ||
|  | In this example, we used the `register` API, which is the core of the Fastify | ||
|  | framework. It is the only way to add routes, plugins, et cetera. | ||
|  | 
 | ||
|  | At the beginning of this guide, we noted that Fastify provides a foundation that | ||
|  | assists with asynchronous bootstrapping of your application. Why is this | ||
|  | important? | ||
|  | 
 | ||
|  | Consider the scenario where a database connection is needed to handle data | ||
|  | storage. The database connection needs to be available before the server is | ||
|  | accepting connections. How do we address this problem? | ||
|  | 
 | ||
|  | A typical solution is to use a complex callback, or promises - a system that | ||
|  | will mix the framework API with other libraries and the application code. | ||
|  | 
 | ||
|  | Fastify handles this internally, with minimum effort! | ||
|  | 
 | ||
|  | Let's rewrite the above example with a database connection. | ||
|  | 
 | ||
|  | 
 | ||
|  | First, install `fastify-plugin` and `@fastify/mongodb`: | ||
|  | 
 | ||
|  | ``` | ||
|  | npm i --save fastify-plugin @fastify/mongodb | ||
|  | ``` | ||
|  | 
 | ||
|  | **server.js** | ||
|  | ```js | ||
|  | // ESM | ||
|  | import Fastify from 'fastify' | ||
|  | import dbConnector from './our-db-connector' | ||
|  | import firstRoute from './our-first-route' | ||
|  | 
 | ||
|  | const fastify = Fastify({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | fastify.register(dbConnector) | ||
|  | fastify.register(firstRoute) | ||
|  | 
 | ||
|  | fastify.listen(3000, function (err, address) { | ||
|  |   if (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  |   // Server is now listening on ${address} | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ```js | ||
|  | // CommonJs | ||
|  | const fastify = require('fastify')({ | ||
|  |   logger: true | ||
|  | }) | ||
|  | 
 | ||
|  | fastify.register(require('./our-db-connector')) | ||
|  | fastify.register(require('./our-first-route')) | ||
|  | 
 | ||
|  | fastify.listen(3000, function (err, address) { | ||
|  |   if (err) { | ||
|  |     fastify.log.error(err) | ||
|  |     process.exit(1) | ||
|  |   } | ||
|  |   // Server is now listening on ${address} | ||
|  | }) | ||
|  | 
 | ||
|  | ``` | ||
|  | 
 | ||
|  | **our-db-connector.js** | ||
|  | ```js | ||
|  | // ESM | ||
|  | import fastifyPlugin from 'fastify-plugin' | ||
|  | import fastifyMongo from '@fastify/mongodb' | ||
|  | 
 | ||
|  | async function dbConnector (fastify, options) { | ||
|  |   fastify.register(fastifyMongo, { | ||
|  |     url: 'mongodb://localhost:27017/test_database' | ||
|  |   }) | ||
|  | } | ||
|  | 
 | ||
|  | // Wrapping a plugin function with fastify-plugin exposes the decorators | ||
|  | // and hooks, declared inside the plugin to the parent scope. | ||
|  | module.exports = fastifyPlugin(dbConnector) | ||
|  | 
 | ||
|  | ``` | ||
|  | 
 | ||
|  | ```js | ||
|  | // CommonJs | ||
|  | const fastifyPlugin = require('fastify-plugin') | ||
|  | 
 | ||
|  | async function dbConnector (fastify, options) { | ||
|  |   fastify.register(require('@fastify/mongodb'), { | ||
|  |     url: 'mongodb://localhost:27017/test_database' | ||
|  |   }) | ||
|  | } | ||
|  | 
 | ||
|  | // Wrapping a plugin function with fastify-plugin exposes the decorators | ||
|  | // and hooks, declared inside the plugin to the parent scope. | ||
|  | module.exports = fastifyPlugin(dbConnector) | ||
|  | 
 | ||
|  | ``` | ||
|  | 
 | ||
|  | **our-first-route.js** | ||
|  | ```js | ||
|  | async function routes (fastify, options) { | ||
|  |   const collection = fastify.mongo.db.collection('test_collection') | ||
|  | 
 | ||
|  |   fastify.get('/', async (request, reply) => { | ||
|  |     return { hello: 'world' } | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.get('/animals', async (request, reply) => { | ||
|  |     const result = await collection.find().toArray() | ||
|  |     if (result.length === 0) { | ||
|  |       throw new Error('No documents found') | ||
|  |     } | ||
|  |     return result | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.get('/animals/:animal', async (request, reply) => { | ||
|  |     const result = await collection.findOne({ animal: request.params.animal }) | ||
|  |     if (!result) { | ||
|  |       throw new Error('Invalid value') | ||
|  |     } | ||
|  |     return result | ||
|  |   }) | ||
|  | 
 | ||
|  |   const animalBodyJsonSchema = { | ||
|  |     type: 'object', | ||
|  |     required: ['animal'], | ||
|  |     properties: { | ||
|  |       animal: { type: 'string' }, | ||
|  |     }, | ||
|  |   } | ||
|  | 
 | ||
|  |   const schema = { | ||
|  |     body: animalBodyJsonSchema, | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.post('/animals', { schema }, async (request, reply) => { | ||
|  |     // we can use the `request.body` object to get the data sent by the client | ||
|  |     const result = await collection.insertOne({ animal: request.body.animal }) | ||
|  |     return result | ||
|  |   }) | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = routes | ||
|  | ``` | ||
|  | 
 | ||
|  | Wow, that was fast! | ||
|  | 
 | ||
|  | Let's recap what we have done here since we've introduced some new concepts. | ||
|  | 
 | ||
|  | As you can see, we used `register` for both the database connector and the | ||
|  | registration of the routes. | ||
|  | 
 | ||
|  | This is one of the best features of Fastify, it will load your plugins in the | ||
|  | same order you declare them, and it will load the next plugin only once the | ||
|  | current one has been loaded. In this way, we can register the database connector | ||
|  | in the first plugin and use it in the second *(read | ||
|  | [here](../Reference/Plugins.md#handle-the-scope) to understand how to handle the | ||
|  | scope of a plugin)*. | ||
|  | 
 | ||
|  | Plugin loading starts when you call `fastify.listen()`, `fastify.inject()` or | ||
|  | `fastify.ready()` | ||
|  | 
 | ||
|  | The MongoDB plugin uses the `decorate` API to add custom objects to the Fastify | ||
|  | instance, making them available for use everywhere. Use of this API is | ||
|  | encouraged to facilitate easy code reuse and to decrease code or logic | ||
|  | duplication. | ||
|  | 
 | ||
|  | To dig deeper into how Fastify plugins work, how to develop new plugins, and for | ||
|  | details on how to use the whole Fastify API to deal with the complexity of | ||
|  | asynchronously bootstrapping an application, read [the hitchhiker's guide to | ||
|  | plugins](./Plugins-Guide.md). | ||
|  | 
 | ||
|  | ### Loading order of your plugins
 | ||
|  | <a id="plugin-loading-order"></a> | ||
|  | 
 | ||
|  | To guarantee consistent and predictable behavior of your application, we highly | ||
|  | recommend to always load your code as shown below: | ||
|  | ``` | ||
|  | └── plugins (from the Fastify ecosystem) | ||
|  | └── your plugins (your custom plugins) | ||
|  | └── decorators | ||
|  | └── hooks | ||
|  | └── your services | ||
|  | ``` | ||
|  | In this way, you will always have access to all of the properties declared in | ||
|  | the current scope. | ||
|  | 
 | ||
|  | As discussed previously, Fastify offers a solid encapsulation model, to help you | ||
|  | build your application as single and independent services. If you want to | ||
|  | register a plugin only for a subset of routes, you just have to replicate the | ||
|  | above structure. | ||
|  | ``` | ||
|  | └── plugins (from the Fastify ecosystem) | ||
|  | └── your plugins (your custom plugins) | ||
|  | └── decorators | ||
|  | └── hooks | ||
|  | └── your services | ||
|  |     │ | ||
|  |     └──  service A | ||
|  |     │     └── plugins (from the Fastify ecosystem) | ||
|  |     │     └── your plugins (your custom plugins) | ||
|  |     │     └── decorators | ||
|  |     │     └── hooks | ||
|  |     │     └── your services | ||
|  |     │ | ||
|  |     └──  service B | ||
|  |           └── plugins (from the Fastify ecosystem) | ||
|  |           └── your plugins (your custom plugins) | ||
|  |           └── decorators | ||
|  |           └── hooks | ||
|  |           └── your services | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Validate your data
 | ||
|  | <a id="validate-data"></a> | ||
|  | 
 | ||
|  | Data validation is extremely important and a core concept of the framework. | ||
|  | 
 | ||
|  | To validate incoming requests, Fastify uses [JSON | ||
|  | Schema](https://json-schema.org/). | ||
|  | 
 | ||
|  | (JTD schemas are loosely supported, but `jsonShorthand` must be disabled first) | ||
|  | 
 | ||
|  | Let's look at an example demonstrating validation for routes: | ||
|  | ```js | ||
|  | const opts = { | ||
|  |   schema: { | ||
|  |     body: { | ||
|  |       type: 'object', | ||
|  |       properties: { | ||
|  |         someKey: { type: 'string' }, | ||
|  |         someOtherKey: { type: 'number' } | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | fastify.post('/', opts, async (request, reply) => { | ||
|  |   return { hello: 'world' } | ||
|  | }) | ||
|  | ``` | ||
|  | This example shows how to pass an options object to the route, which accepts a | ||
|  | `schema` key that contains all of the schemas for route, `body`, `querystring`, | ||
|  | `params`, and `headers`. | ||
|  | 
 | ||
|  | Read [Validation and | ||
|  | Serialization](../Reference/Validation-and-Serialization.md) to learn more. | ||
|  | 
 | ||
|  | ### Serialize your data
 | ||
|  | <a id="serialize-data"></a> | ||
|  | 
 | ||
|  | Fastify has first class support for JSON. It is extremely optimized to parse | ||
|  | JSON bodies and to serialize JSON output. | ||
|  | 
 | ||
|  | To speed up JSON serialization (yes, it is slow!) use the `response` key of the | ||
|  | schema option as shown in the following example: | ||
|  | ```js | ||
|  | const opts = { | ||
|  |   schema: { | ||
|  |     response: { | ||
|  |       200: { | ||
|  |         type: 'object', | ||
|  |         properties: { | ||
|  |           hello: { type: 'string' } | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | fastify.get('/', opts, async (request, reply) => { | ||
|  |   return { hello: 'world' } | ||
|  | }) | ||
|  | ``` | ||
|  | By specifying a schema as shown, you can speed up serialization by a factor of | ||
|  | 2-3. This also helps to protect against leakage of potentially sensitive data, | ||
|  | since Fastify will serialize only the data present in the response schema. Read | ||
|  | [Validation and Serialization](../Reference/Validation-and-Serialization.md) to | ||
|  | learn more. | ||
|  | 
 | ||
|  | ### Parsing request payloads
 | ||
|  | <a id="request-payload"></a> | ||
|  | 
 | ||
|  | Fastify parses `'application/json'` and `'text/plain'` request payloads | ||
|  | natively, with the result accessible from the [Fastify | ||
|  | request](../Reference/Request.md) object at `request.body`. | ||
|  | 
 | ||
|  | The following example returns the parsed body of a request back to the client: | ||
|  | 
 | ||
|  | ```js | ||
|  | const opts = {} | ||
|  | fastify.post('/', opts, async (request, reply) => { | ||
|  |   return request.body | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | Read [Content-Type Parser](../Reference/ContentTypeParser.md) to learn more | ||
|  | about Fastify's default parsing functionality and how to support other content | ||
|  | types. | ||
|  | 
 | ||
|  | ### Extend your server
 | ||
|  | <a id="extend-server"></a> | ||
|  | 
 | ||
|  | Fastify is built to be extremely extensible and minimal, we believe that a | ||
|  | bare-bones framework is all that is necessary to make great applications | ||
|  | possible. | ||
|  | 
 | ||
|  | In other words, Fastify is not a "batteries included" framework, and relies on | ||
|  | an amazing [ecosystem](./Ecosystem.md)! | ||
|  | 
 | ||
|  | ### Test your server
 | ||
|  | <a id="test-server"></a> | ||
|  | 
 | ||
|  | Fastify does not offer a testing framework, but we do recommend a way to write | ||
|  | your tests that uses the features and architecture of Fastify. | ||
|  | 
 | ||
|  | Read the [testing](./Testing.md) documentation to learn more! | ||
|  | 
 | ||
|  | ### Run your server from CLI
 | ||
|  | <a id="cli"></a> | ||
|  | 
 | ||
|  | Fastify also has CLI integration thanks to | ||
|  | [fastify-cli](https://github.com/fastify/fastify-cli). | ||
|  | 
 | ||
|  | First, install `fastify-cli`: | ||
|  | 
 | ||
|  | ``` | ||
|  | npm i fastify-cli | ||
|  | ``` | ||
|  | 
 | ||
|  | You can also install it globally with `-g`. | ||
|  | 
 | ||
|  | Then, add the following lines to `package.json`: | ||
|  | ```json | ||
|  | { | ||
|  |   "scripts": { | ||
|  |     "start": "fastify start server.js" | ||
|  |   } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | And create your server file(s): | ||
|  | ```js | ||
|  | // server.js | ||
|  | 'use strict' | ||
|  | 
 | ||
|  | module.exports = async function (fastify, opts) { | ||
|  |   fastify.get('/', async (request, reply) => { | ||
|  |     return { hello: 'world' } | ||
|  |   }) | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | Then run your server with: | ||
|  | ```bash | ||
|  | npm start | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Slides and Videos
 | ||
|  | <a id="slides"></a> | ||
|  | 
 | ||
|  | - Slides | ||
|  |   - [Take your HTTP server to ludicrous | ||
|  |     speed](https://mcollina.github.io/take-your-http-server-to-ludicrous-speed) | ||
|  |     by [@mcollina](https://github.com/mcollina) | ||
|  |   - [What if I told you that HTTP can be | ||
|  |     fast](https://delvedor.github.io/What-if-I-told-you-that-HTTP-can-be-fast) | ||
|  |     by [@delvedor](https://github.com/delvedor) | ||
|  | 
 | ||
|  | - Videos | ||
|  |   - [Take your HTTP server to ludicrous | ||
|  |     speed](https://www.youtube.com/watch?v=5z46jJZNe8k) by | ||
|  |     [@mcollina](https://github.com/mcollina) | ||
|  |   - [What if I told you that HTTP can be | ||
|  |     fast](https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/) | ||
|  |     by [@delvedor](https://github.com/delvedor) |