Skip to content

Commit

Permalink
Merge pull request #462 from fastify/improve-getting-started
Browse files Browse the repository at this point in the history
Improve getting started
  • Loading branch information
delvedor committed Nov 14, 2017
2 parents 2bb28f6 + f5e0687 commit 2b962a8
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 87 deletions.
214 changes: 152 additions & 62 deletions docs/Getting-Started.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<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 introduction with examples and links to other parts of the documentation.
Let's start!

<a name="install"></a>
### Install
```
npm i fastify --save
```
<a name="first-server"></a>
### Your first server
Let's write a *Hello world* server!
Let's write our first server:
```js
// Require the framework and instantiate it
const fastify = require('fastify')()
Expand All @@ -20,97 +25,186 @@ fastify.get('/', function (request, reply) {
// Run the server!
fastify.listen(3000, function (err) {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
})
```

Do you prefer to use `async/await`? Please review [Routes#async-await](https://github.com/fastify/fastify/blob/master/docs/Routes.md#async-await)!

<a name="schema"></a>
### Schema serialization
Fastify is designed for performance. To truly turbocharge our server, we can serialize responses according to the [JSON Schema](http://json-schema.org/) standard:
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
const fastify = require('fastify')()

const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}

// Declare a route with an output schema
fastify.get('/', opts, function (request, reply) {
reply.send({ hello: 'world' })
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})

fastify.listen(3000, function (err) {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
})
```

*To learn more about using serialization and validation, see [Validation and Serialization](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md)!*
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 handle multiple files, asynchronous bootstrapping and the architecture of your code.
Fastify offers an easy platform that helps solve all of problems, and more.

<a name="register"></a>
### Register
As you can see, using Fastify is very easy and handy. Obviously, registering all of your routes in the same file is not a good idea, so Fastify offers a utility to solve this problem, `register`:
<a name="first-plugin"></a>
### Your first plugin
As with JavaScript 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 (checkout the [route declaration](https://github.com/fastify/fastify/blob/master/docs/Routes.md) docs).
```js
/* server.js */

const fastify = require('fastify')()

fastify.register(require('./route'))
fastify.register(require('./our-first-route'))

const opts = {
hello: 'world',
something: true
fastify.listen(3000, function (err) {
if (err) throw err
fastify.log.info(`server listening on ${fastify.server.address().port}`)
})
```

```js
// our-first-route.js

async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}

fastify.register([
require('./another-route'),
require('./yet-another-route')
], opts, function (err) {
if (err) throw err
module.exports = routes
```
In this example we used the `register` API. This API is the core of the Fastify framework, and is the only way to register routes, plugins and so on.

At the beginning of this guide we noted that Fastify provides a foundation that assists with the asynchronous bootstrapping of your an application. Why this is important?
Consider the scenario where a database connection is needed to handle data storage. Obviously the database connection needs to be available prior to the server accepting connections. How do we address this problem?
A typical solution is to use a complex callback, or promises, 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.
*(we will use a simple example, for a robust solution consider using [`fastify-mongo`](https://github.com/fastify/fastify-mongodb) or another in the Fastify [ecosystem](https://github.com/fastify/fastify/blob/master/docs/Ecosystem.md))*
```js
const fastify = require('fastify')()

fastify.register(require('./our-db-connector'), {
url: 'mongodb://mongo/db'
})
fastify.register(require('./our-first-route'))

fastify.listen(8000, function (err) {
fastify.listen(3000, function (err) {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
fastify.log.info(`server listening on ${fastify.server.address().port}`)
})
```

```js
/* route.js */
// our-db-connector.js
const MongoClient = require('mongodb').MongoClient

module.exports = function (fastify, options, next) {
fastify.get('/', function (req, reply) {
reply.send({ hello: 'world' })
})
next()
async function db (fastify, options) {
const url = options.url
delete options.url

const db = await MongoClient.connect(url, options)
fastify.decorate('mongo', db)
}

module.exports = db
```

or with `async/await`:
```js
/* route.js */
// our-first-route.js

async function routes (fastify, options) {
const collection = fastify.mongo.collection('test')

module.exports = async function (fastify, options) {
fastify.get('/', function (req, reply) {
reply.send({ hello: 'world' })
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})

fastify.get('/search/:id', async (request, reply) => {
return await collection.findOne({ id: req.params.id })
})
}

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` both for the database connector and the routes registration.
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.

We have used the `decorate` api API. Let's take a moment to understand what it is and how it works. A scenario is to use the same code/library in different parts of an application. A solution is to require the code/library that it is needed. it This works, but is annoying because of duplicated code repeated and, if needed, long refactors.
To solve this Fastify offers the `decorate` API, which adds custom objects to the Fastify namespace, so that they can be used everywhere.

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](https://github.com/fastify/fastify/blob/master/docs/Plugins.md).

<a name="validate-data"></a>
### Validate your data
Data validation is extremely important and is a core concept of the framework.
To validate incoming requests, Fastify uses [JSON Schema](http://json-schema.org/).
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](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md) to learn more.

<a name="serialize-data"></a>
### Serialize your data
Fastify has first class support for JSON. It is extremely optimized to parse a JSON body and to serialize JSON output.
To speed up JSON serialization (yes, it is slow!) use the `response` key of the schema option like so:
```js
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}

fastify.get('/', opts, async (request, reply) => {
return { hello: 'world' }
})
```
Simply by specifying a schema as shown, a speed up your of serialization by 2x or even 3x can be achieved. This also helps protect against leaking of sensitive data, since Fastify will serialize only the data present in the response schema.
Read [Validation and Serialization](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md) to learn more.

<a name="extend-server"></a>
### Extend your server
Fastify is built to be extremely extensible and very minimal, We believe that a bare minimum 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](https://github.com/fastify/fastify/blob/master/docs/Ecosystem.md)!

<a name="test-server"></a>
### Test your server
Fastify does not offer a testing framework, but we do recommend a way to write your tests that uses the features and the architecture of Fastify.
Read the [testing](https://github.com/fastify/fastify/blob/master/docs/Testing.md) documentation to learn more!

<a name="cli"></a>
### Run from CLI
You can also run Fastify from the CLI thanks to [fastify-cli](https://github.com/fastify/fastify-cli). It's very easy! Simply add the following lines to your `package.json`:
### Run your server from CLI
Fastify also has CLI integration thanks to [fastify-cli](https://github.com/fastify/fastify-cli). It's very easy!
Simply add the following lines to `package.json`:
```json
{
"scripts": {
Expand All @@ -124,11 +218,10 @@ And create your server file(s):
// server.js
'use strict'

module.exports = function (fastify, opts, next) {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
module.exports = async function (fastify, opts) {
fastify.get('/', async (req, reply) => {
return { hello: 'world' }
})
next()
}
```

Expand All @@ -145,7 +238,4 @@ npm start

- Videos
- [Take your HTTP server to ludicrous speed](https://www.youtube.com/watch?v=5z46jJZNe8k) by [@mcollina](https://github.com/mcollina)

<a name="next"></a>
### Next
Do you want to learn more? Check out the documentation folder located [here](https://github.com/fastify/fastify/blob/master/docs/)!
- [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)
23 changes: 21 additions & 2 deletions docs/Plugins-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ Your routes, your utilities and so on are all plugins. And to add a new plugin,
```js
fastify.register(
require('./my-plugin'),
{ options },
callback
{ options }
)
```
`register` creates for you a new Fastify context, this means that if you do any change to the Fastify instance, that change(s) will not be reflected into the context's ancestors. In other words, encapsulation!
Expand Down Expand Up @@ -271,6 +270,26 @@ module.exports = fp(dbPlugin)
```
You can also tell to `fastify-plugin` to check the installed version of Fastify, in case of you need a specific api.

<a name="handle-errors"></a>
## Handle errors
It can happen that one of your plugins could fail during the startup. Maybe you expect it and you have a custom logic that will be triggered in that case. How can you do this?
The `after` api is what you need. `after` simply register a callback that will be executed just after a register, and it can take up to three parameters.
The callback changes basing on the parameters your are giving:

1. If no parameter is given to the callback and there is an error, that error will be passed to the next error handler.
1. If one parameter is given to the callback, that parameter will be the error object.
1. If two parameters are given to the callback, the first will be the error object, the second will be the done callback.
1. If three parameters are given to the callback, the first will be the error object, the second will be the top level context unless you have specified both server and override, in that case the context will be what the override returns, and the third the done callback.

Let's see how use it:
```js
fastify
.register(require('./database-connector'))
.after(err => {
if (err) throw err
})
```

<a name="start"></a>
## Let's start!
Awesome, now you know everything you need to know about Fastify and its plugin system to start building your first plugin, and please if you do, tell us! We will add it to the [*ecosystem*](https://github.com/fastify/fastify#ecosystem) section of our documentation!
Expand Down

0 comments on commit 2b962a8

Please sign in to comment.