Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve getting started #462

Merged
merged 1 commit into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=> <a id="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)*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is out of the context. Maybe a recommendation is better for this tips

```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) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the reply parameter even need to be supplied?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you need to set some custom header o change the status code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then maybe a note about it and why it isn't being used here would be good to have.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a note could be a nice idea, but I think that this document should provide just a getting started, we have provided the link to other parts of the documentation that explains better the usage of every api. I don't think that add here too many info could be a good idea.

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a new application is how handle multiple files => a new application is how handling multiple files

Fastify offers an easy platform that helps solve all of problems, and more.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that helps solve => that helps solving

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or add a "these" between "of" and "problems".


<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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=> <a id="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 })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the query doesn't return any document, which is the behaviour? I know that this is an example but maybe:

const Boom = require('boom')
fastify.get('/search/:id', async (request, reply) => {
  const doc = await collection.findOne({ id: req.params.id })
  if (!doc) throw Boom.notFound()
  return doc
})

It easies to introduce the build-in error handing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very basic example, I don't want to make it more complex than needed. Here we are just showing the async/await support and how use a db connector with decorate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of "incomplete" examples. Anyway I understand this is out of the scope of "Your first plugin" section. But I'd like to have a section in this file that describes the error handling: it is a great feature!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of "incomplete" examples

Me too, but your proposal is out of scope. If you show the usage of Boom in a getting started example a user will probably think that is required, which is completely false.

The error handling is described in the plugin guide.

})
}

module.exports = routes
```

Wow, that was fast!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that was fast easy!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast was not an error, what I wanted to say is that if you use the apis that Fastify provides, develop async code is fast (and easy :P).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=> <a id="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