Fastify is a framework designed for back-end web development. It offers a more lightweight alternative to heavier Node.js API frameworks, such as Hapi and Express. From July 2020, Fastify has released its third version of the framework.
This third version comes with improved validation abilities to verify incoming and outgoing requests, as request parameters. Moreover, the third version of the framework consolidates its throughput claims of being the fastest Node.js framework compared with Koa, Resitfy, Hapi, and Express. More information can be found on the benchmarks page.
Fastify has gained a lot of popularity due to its lightweight design. However, a lot of attention goes to its plugin ecosystem. Fastify has adopted the idea that everything is a plugin, whereas with JavaScript, everything is an object. This allows you to quickly encapsulate functionality for your project as a plugin and distribute it so other projects can use your code.
Let’s get started with this tutorial. You’ll learn the following aspects of Fastify:
To follow this tutorial, you’ll need:
Next, make sure to create an empty Node.js project. If you don’t have one yet, you can use the following command to set up your project:
<span>npm init -y </span>
Lastly, we want to add this Fastify dependency to our project:
<span>npm i fastify --save </span>
All good? Let’s create our basic API setup in the next step.
First, let’s create our basic API setup. To get started, we need to create a new file called index.js within our project root:
<span>touch index.js </span>
Next, let’s add the basic server setup. Copy the code below:
<span>npm init -y </span>
There are a couple of things happening here. We first load the Fastify application object and enable logging. Next, we declare a root route that replies with a JSON response. The last part of the code snippet shows that we’re listening on port 3000 for the application to receive requests.
Let’s validate if your basic server setup works. First, we need to start the server by running the index.js file:
<span>npm i fastify --save </span>
Thereafter, navigate to http://localhost:3000 in your browser. You should see the following response:
<span>touch index.js </span>
Success? Let’s go to Step 2 to define different CRUD routes.
An API is useless with only GET routes. Let’s define more routes for handling blogs. Therefore, let’s create the following routes:
The first thing to do is creating a blog controller.
To keep our code clean, let’s define a controller folder in the project root. Here, we create a file called blogs.js.
This file contains some demo data to avoid complicating this tutorial with a database integration. Therefore, we use an array containing blog objects which each contain an ID and title field.
Moreover, we define the different handlers for all the above routes in this file. A handler always accepts a req (request) and reply parameter. The request parameter is useful to access request parameters or request body data.
Add the following code to your /controller/blogs.js file:
<span>// Require the framework and instantiate it </span><span>const app = require('fastify')({ </span> <span>logger: true </span><span>}) </span> <span>// Declare a route </span>app<span>.get('/', function (req<span>, reply</span>) { </span> reply<span>.send({ hello: 'world' }) </span><span>}) </span> <span>// Run the server! </span>app<span>.listen(3000, (err<span>, address</span>) => { </span> <span>if (err) { </span> app<span>.log.error(err) </span> process<span>.exit(1) </span> <span>} </span> app<span>.log.info(<span>`server listening on <span>${address}</span>`</span>) </span><span>}) </span>
Note how we can access the request parameter for routes such as /api/blogs/:id via req.params.id. For POST and PUT routes, we can access the body of the request via req.body.
In step 2.2, we’ll connect the route handlers to the route objects.
Again, to keep our code clean, let’s define a routes folder in the project root. Here, we create a file called blogs.js. This file holds the routes object for our blog routes:
<span>node index.js </span>
Luckily, Fastify allows us to define an array containing route objects. Here, we can couple the handlers we’ve defined previously to the different routes. Don’t forget to require the blogs controller. Let’s take a look:
<span>{ </span> <span>"hello": "world" </span><span>} </span>
Now we’ve defined all routes. However, Fastify doesn’t know about these routes. The next step shows how you can register routes with your Fastify application object.
In this step, we’ll register Fastify routes to the app object. First, we load all the blog routes. Next, we loop over all the routes to register them one by one:
<span>npm init -y </span>
Done? It’s time to validate if the blog routes work. Spin up the server using node index.js and visit http://localhost:3000/blogs/1 to get the first blog from the demo data. You should see the following result:
<span>npm i fastify --save </span>
All good? Let’s learn in Step 3 how to add schema validation to requests and responses.
This step teaches you how to add schema validation to your project. We can make use of the schema key in our routes definition to pass a validation schema to a particular route.
Let’s start with defining a schema for the route /api/blogs/:id to validate the request parameter and response. Requirements?
Add the following validation object to your routes/blogs.js file:
<span>touch index.js </span>
To connect the validation object to our route, we have to define the schema key. Look for the /api/blogs/:id route in the routes array and change the object accordingly:
<span>// Require the framework and instantiate it </span><span>const app = require('fastify')({ </span> <span>logger: true </span><span>}) </span> <span>// Declare a route </span>app<span>.get('/', function (req<span>, reply</span>) { </span> reply<span>.send({ hello: 'world' }) </span><span>}) </span> <span>// Run the server! </span>app<span>.listen(3000, (err<span>, address</span>) => { </span> <span>if (err) { </span> app<span>.log.error(err) </span> process<span>.exit(1) </span> <span>} </span> app<span>.log.info(<span>`server listening on <span>${address}</span>`</span>) </span><span>}) </span>
Let’s do the same for adding a blog POST /api/blogs. Here, we want to verify if the req.body object contains a title parameter. Let’s take a look:
<span>node index.js </span>
Next, we have to connect the validation object again to the correct route:
<span>{ </span> <span>"hello": "world" </span><span>} </span>
To verify our validation, let’s retrieve the blog with ID 3. Open your browser at http://localhost:3000/api/blogs/3. You should see the following response:
<span>// Demo data </span><span>let blogs = [ </span> <span>{ </span> <span>id: 1, </span> <span>title: 'This is an experiment' </span> <span>}, </span> <span>{ </span> <span>id: 2, </span> <span>title: 'Fastify is pretty cool' </span> <span>}, </span> <span>{ </span> <span>id: 3, </span> <span>title: 'Just another blog, yea!' </span> <span>} </span><span>] </span> <span>// Handlers </span><span>const getAllBlogs = async (req<span>, reply</span>) => { </span> <span>return blogs </span><span>} </span> <span>const getBlog = async (req<span>, reply</span>) => { </span> <span>const id = Number(req.params.id) // blog ID </span> <span>const blog = blogs.find(blog => blog.id === id) </span> <span>return blog </span><span>} </span> <span>const addBlog = async (req<span>, reply</span>) => { </span> <span>const id = blogs.length + 1 // generate new ID </span> <span>const newBlog = { </span> id<span>, </span> <span>title: req.body.title </span> <span>} </span> blogs<span>.push(newBlog) </span> <span>return newBlog </span><span>} </span> <span>const updateBlog = async (req<span>, reply</span>) => { </span> <span>const id = Number(req.params.id) </span> blogs <span>= blogs.map(blog => { </span> <span>if (blog.id === id) { </span> <span>return { </span> id<span>, </span> <span>title: req.body.title </span> <span>} </span> <span>} </span> <span>}) </span> <span>return { </span> id<span>, </span> <span>title: req.body.title </span> <span>} </span><span>} </span> <span>const deleteBlog = async (req<span>, reply</span>) => { </span> <span>const id = Number(req.params.id) </span> blogs <span>= blogs.filter(blog => blog.id !== id) </span> <span>return { msg: <span>`Blog with ID <span>${id}</span> is deleted`</span> } </span><span>} </span> module<span>.exports = { </span> getAllBlogs<span>, </span> getBlog<span>, </span> addBlog<span>, </span> updateBlog<span>, </span> deleteBlog <span>} </span>
Now, let’s make a mistake and change the params validation for the id field from sting to object like so:
<span>mkdir routes </span><span>cd routes </span><span>touch blogs.js </span>
When requesting the same resource from your API, you’ll receive the following error message.
<span>const blogController = require('../controller/blogs'); </span> <span>const routes = [{ </span> <span>method: 'GET', </span> <span>url: '/api/blogs', </span> <span>handler: blogController.getAllBlogs </span> <span>}, </span> <span>{ </span> <span>method: 'GET', </span> <span>url: '/api/blogs/:id', </span> <span>handler: blogController.getBlog </span> <span>}, </span> <span>{ </span> <span>method: 'POST', </span> <span>url: '/api/blogs', </span> <span>handler: blogController.addBlog </span> <span>}, </span> <span>{ </span> <span>method: 'PUT', </span> <span>url: '/api/blogs/:id', </span> <span>handler: blogController.updateBlog </span> <span>}, </span> <span>{ </span> <span>method: 'DELETE', </span> <span>url: '/api/blogs/:id', </span> <span>handler: blogController.deleteBlog </span> <span>} </span><span>] </span>module<span>.exports = routes </span>
Do you see the error? Good! Let’s revert the change to string to avoid future errors and move to the next step.
Here, let’s make use of Fastify’s rich plugin ecosystem. You can find plugins that help you with various tasks, such as database integrations or authorization setups. Why would you spend time writing authorization from scratch while you can make use of Fastify plugins? Often, you want to look for packages outside of Fastify’s ecosystem that help you with certain problems or tasks. However, by providing a rich plugin ecosystem, Fastify becomes a one-stop solution that definitely improves the developer experience!
A quick note about plugins: You can create your own plugins to encapsulate functionality. Moreover, you can load those plugins to your Fastify application object. By default, Fastify will first load plugins from the Fastify ecosystem. Afterward, custom plugins are loaded.
Ok, let’s get practical! I would like to use the fastify-env plugin, which helps you with loading environment variables and setting defaults for each variable. Therefore, let’s add this dependency to our project:
<span>npm init -y </span>
Next, we can load the dependency after loading the Fastify application object in the index.js file. Your index.js file looks like this:
<span>npm i fastify --save </span>
Note that we have to define an options object that tells the fastify-env plugin what env variables to look for and which defaults to set. Here, I want to load a PORT variable with a default value of 1000.
By default, the fastify-env plugin will make all environment variables available via the Fastify app object like so: app.config.PORT. Why? The fastify-env plugin attaches the loaded configurations to the confKey, which by default is set to config. However, if you wish, you can change this to another key.
Start the project with node index.js and monitor the output. You should see the PORT variable being printed in your terminal.
Other interesting plugins to use?
Lastly, let’s define some hooks. From the Fastify hooks documentation, we can read the following. “Hooks are registered with the fastify.addHook method and allow you to listen to specific events in the application or request/response lifecycle. You have to register a hook before the event is triggered, otherwise the event is lost.”
Make sure to define hooks before you define any routes:
<span>touch index.js </span>
As you can see, the addHook function first accepts the hook you want to listen for. In our example, we want to listen for new routes being registered with the application. Next, the callback function accepts a routeOptions argument which contains a lot of information, such as the route URL or route method.
Specific details for the onRoute hook can be found in the documentation.
Let’s start the API with node index.js to see which routes have been registered. Your terminal output should look like this:
<span>// Require the framework and instantiate it </span><span>const app = require('fastify')({ </span> <span>logger: true </span><span>}) </span> <span>// Declare a route </span>app<span>.get('/', function (req<span>, reply</span>) { </span> reply<span>.send({ hello: 'world' }) </span><span>}) </span> <span>// Run the server! </span>app<span>.listen(3000, (err<span>, address</span>) => { </span> <span>if (err) { </span> app<span>.log.error(err) </span> process<span>.exit(1) </span> <span>} </span> app<span>.log.info(<span>`server listening on <span>${address}</span>`</span>) </span><span>}) </span>
Got the same output? Success! At the same time, this was the end of the Fastify tutorial. Let’s wrap up this project with a short conclusion.
Fastify is a great, light-weight project that allows you to make use of its rich plugin ecosystem. Instead of creating functionality from scratch, you can make use of existing plugins. In other words, Fastify acts as a one-stop shop for developers, definitely improving the developer experience.
Personally, I like the Fastify hooks functionality as you can listen for various lifecycle events within your application.
To learn more about Fastify, check out the following documentation pages:
You can also check out the repo for this introduction on GitHub.
Before you start creating a REST API with Fastify, you need to have a basic understanding of JavaScript and Node.js. You should also have Node.js and npm installed on your computer. Node.js is a JavaScript runtime that allows you to run JavaScript on your server, while npm is a package manager for Node.js that allows you to install and manage packages that your project depends on.
You can install Fastify using npm, the Node.js package manager. Open your terminal and run the following command: npm install fastify. This will install Fastify in your current project directory. Make sure you are in the correct directory before running the command.
To create a server with Fastify, you first need to require the Fastify module in your application. Then, you can create a new Fastify instance. Here is a basic example:
const fastify = require('fastify')({ logger: true })
fastify.listen(3000, (err, address) => {
if (err) throw err
fastify.log.info(`server listening on ${address}`)
})
In Fastify, you define routes using the route method. This method takes an object as an argument, which defines the route’s properties. Here is an example:
fastify.route({
method: 'GET',
url: '/',
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
Fastify provides a built-in error handling mechanism. You can use the reply object’s code and send methods to send an error response. Here is an example:
fastify.get('/error', function (request, reply) {
reply.code(500).send(new Error('Internal server error'))
})
Fastify has a powerful plugin architecture that allows you to extend its functionality. You can use the register method to register a plugin. Here is an example:
fastify.register(require('fastify-cors'), {
origin: true
})
Fastify provides a built-in validation and serialization library called fastify-schema. You can use this library to validate incoming requests and serialize outgoing responses. Here is an example:
fastify.route({
method: 'POST',
url: '/validate',
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string' }
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
Fastify provides a inject method that allows you to simulate HTTP requests and responses for testing purposes. Here is an example:
fastify.inject({
method: 'GET',
url: '/',
}, (err, response) => {
console.log(response.payload)
})
You can deploy your Fastify application like any other Node.js application. You can use platforms like Heroku, AWS, or Google Cloud Platform. Make sure to set the NODE_ENV environment variable to production when deploying your application.
Fastify supports both callbacks and promises for handling asynchronous code. You can use the async/await syntax in your route handlers to write asynchronous code in a more readable way. Here is an example:
fastify.get('/async', async function (request, reply) {
const result = await someAsyncOperation()
return result
})
The above is the detailed content of How to Create Your First REST API with Fastify. For more information, please follow other related articles on the PHP Chinese website!