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
						
					
					
				| <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)
 |