How to Build a REST API with Fastify?


Fastify is a framework that is mainly designed to do backend development in JavaScript. It is one of the lightest backend framework that one can work with, and it is one of the main reasons why it is preferred if you want to avoid the heavier node frameworks like Express and Hapi.

Since its inception, multiple versions of Fastify have been released. In the latest version, we get the capability to even verify the incoming and outgoing requests as well as the request parameters. It might not come as a surprise that the people who developed Fastify claim that it is the fastest Node.js framework that you can work with, as compared to the other frameworks like Koa, Hapi, and Express.

The Fastify frameworks has gained a lot of popularity mainly because of its lightweight design. One thing that differentiates Fastify from the native frameworks is that it considers everything as a plugin whereas in JavaScript, we consider everything as an object. This in turn gives us the privilege to quickly encapsulate functionality for the project as a plugin and distribute it to other projects as well.

In this tutorial, we will learn the following aspects of the Fastify framework with the help of examples:

  • How to create a simple Fastify Server

  • How to define API routes in Fastify

  • How to add schema validation to our requests

  • How to define Hooks

Requirement and Installation

The first step in order to build a REST API using Fastify is to create a project and install the Fastify as a dependency in our project.

The first things that you will require are:

  • Node.js latest versions

  • A tool to test the end points like PostMan or cURL.

In order to verify the version of node, you can run the command shown below.

node -v

Once you run the above command, you will get the following output on the terminal.

v16.16.0

If you somehow don't get the same output, you need to first install "node" on your local machine and then continue with the commands shown below.

The next step is to create an empty Node project. If you don't have a project yet, then you can initiate the same with the command shown below −

npm init -y

Once you run the above command in the terminal, a "package.json" file will be created that will track all the dependencies along with the scripts that you may require.

In order to be able to use the Fastify framework, we need to import it as a dependency and we can do that with the help of the command shown below.

npm i fastify --save

Once we do that, we should see Fastify mentioned in the dependencies inside our "package.json" file.

Now we are all setup to move on the different stages of building a REST API.

How to Create a Simple Fastify Server?

To create a simple Fastify server, we first need to create a JS file. Let's say that we want to name the file "index.js". To create the same file, run the following command in your terminal −

touch index.js

index.js

Now open your project in your favorite code editor, and write the following code in the "index.js" file.

// to require the framework
const app = require('fastify')({
   logger: true
})

// to declare a single route
app.get('/', function (req, reply) {
   reply.send({
      Welcome: 'TutorialsPoint'
   })
})

// Run the server!
app.listen({ port: 3000 }, (err, address) => {
   if (err) {
      app.log.error(err)
      process.exit(1)
   }
   app.log.info(`The server is listening on ${address}`)
})

In the above example, I loaded the Fastify application object and then in that object, I enabled the logging. Later on, I am declaring a single route that replies with a single response, i.e., "Welcome: TutorialsPoint". The last code block depicts the case where we are listening, on port 3000.

In order to run the above code, we need to run the following command in the terminal.

node index.js

Once we run the above command, our server will be up and running on the following endpoint.

http://localhost:3000

Now to test it out, we can either use PostMan or cURL or simply visit the browser, as it is a simple GET Request.

I will use the cURL command. Consider the command shown below −

curl http://localhost:3000 

Once you run the above command, you will get the following output on the terminal.

{"Welcome":"TutorialsPoint"}

The first step of creating a simple Fastify server is done, now let's learn how we can define routes in our API.

How to Define API Routes in Fastify?

There's no point in having an API if it doesn't have multiples routes in it. In our API, we will have multiple routes, as our REST API is about getting details of different books and their authors and their title.

In our REST API example, we will define the following routes.

  • GET – all books at /api/books

  • GET – a single book at /api/book/:id

  • POST – add a book at /api/books

  • PUT – update a book at /api/books/:id

  • DELETE – delete a book at /api/delete/:id

Before defining routes, it is necessary that we define controllers for these routes.

Creating Books Controller

To keep our code modular and clean, let's create a directory named controller and inside that directory we can create a file named books.js.

books.js

In the books.js file, paste the code that is shown below −

let books = [{
   id: 1,
   title: 'Maharana Pratap : The Invincible Warrior',
   author: 'Rima Hooja'
},
{
   id: 2,
   title: 'Prithviraj Chauhan - A Light on the Mist in History',
   author: 'Virendra Singh Rathore'
},
{
   id: 3,
   title: 'Rani Laxmibai: Warrior-Queen of Jhansi',
   author: 'Pratibha Ranade'
}
]

// Handlers
const getAllBooks = async (req, reply) => {  
}
const getBook = async (req, reply) => {
   const id = Number(req.params.id) 
   const book = books.find(book => book.id === id)
   return book
}
const addBook = async (req, reply) => {
   const id = books.length + 1
   const newBook = {
      id,
      title: req.body.title,
      author: req.body.author
   }
   books.push(newBook)
   return newBook
}

const updateBook = async (req, reply) => {
   const id = Number(req.params.id)
   books = books.map(book => {
      if (book.id === id) {
         return {
            id,
            title: req.body.title,
            author: req.body.author
         }
      }
   })
   return {
      id,
      title: req.body.title
   }
}

const deleteBook = async (req, reply) => {
   const id = Number(req.params.id)

   books = books.filter(book => book.id !== id)
   return {
      msg: `Blog with ID ${id} is deleted`
   }
}
module.exports = {
   getAllBooks,
   getBook,
   addBook,
   updateBook,
   deleteBook
}

In the above code, there are different handlers that are defined. These are −

  • getAllBooks − To get the response that contains all the books

  • getBook − To get a particular book from the id of the book.

  • addBook − To add a book to the book array of book objects.

  • updateBook − To update a book

  • deleteBook − To delete a book from the array of book objects.

It should be noted that to save time and keep the controller simple, I made use of an array of objects to store the book information, instead of making use of a database.

The next step is to create routes so that we can make use of these controller functions inside them. To create routes, we will follow a similar folder structure.

Let's create a directory named "routes" in the root directory of the project. Inside the "routes" directory, let's create a file named "books.js".

books.js

Now, paste the following code in the "books.js" file.

const booksController = require('../controller/books');

const routes = [{
method: 'GET',
   url: '/api/books',
   handler: booksController.getAllBooks
},
{
   method: 'GET',
   url: '/api/books/:id',
   handler: booksController.getBook
},
{
   method: 'POST',
   url: '/api/books',
   handler: booksController.addBook
},
{
   method: 'PUT',
   url: '/api/books/:id',
   handler: booksController.updateBook
},
{
   method: 'DELETE',
   url: '/api/books/:id',
   handler: booksController.deleteBook
}
]
module.exports = routes

In the above code, we have defined all the routes that we mentioned above, and also in each of the routes, I have an associated handler that takes care of the route.

Now the only step left before we can run the routes and test them, is to first add these routes to the app object, and we do that in our index.js file that is present inside the root directory of the project.

index.js

// to require the framework
const app = require('fastify')({
   logger: true
})

// to declare a single route
app.get('/', function (req, reply) {
   reply.send({
      Welcome: 'TutorialsPoint'
   })
})

// Register routes to handle blog posts
const bookRoutes = require('./routes/books')

bookRoutes.forEach((route, index) => {
   app.route(route)
})

// Run the server!
app.listen(3000, (err, address) => {
   if (err) {
      app.log.error(err)
      process.exit(1)
   }
   app.log.info(`The server is listening on ${address}`)
})

Now, defining routes is done. And to test these routes, we first need to run the application with the command shown below −

index.js

Once we run the above command, we can then open our browser and hit the url, http://localhost:3000/api/books/1 which will invoke the getBook handler function inside our controller/books.js file and will return us the book with "id = 1".

How to Add Schema Validations to Our Request?

In the code in the previous section, we had no request validations, which means that we can pass anything in the request and it will be considered valid and enter our code, but that's not what we generally want.

Now, let's say that we want to make sure that the id, that we pass into the getBook endpoint, is of type object only and not of any other type and for that we can write a request validation inside the controller.

Consider the code snippet shown below −

const getBookValidation = {
   params: {
      id: { type: 'object' }
   },
   response: {
      200: {
         type: 'object',
         properties: {
         id: { type: 'integer' },
            title: { type: 'string' },
            author: { type: 'string'}
         }
      }
   }
}

In the above validation, we are making sure that the id field that is passed in the params is of type object and not of any other type.

Also, we need to add the function getBookValidation in modules.export in the controller/books.js as well.

Once the controller part is done, the next step is to add the function in the routes so that when we get a request to that route, our validation works. In order to do that, we need to write the following line shown below inside the getBook route.

schema: booksController.getBookValidation,

books.js

const booksController = require('../controller/books');

const routes = [{
   method: 'GET',
   url: '/api/books',
   handler: booksController.getAllBooks
},
{
   method: 'GET',
   url: '/api/books/:id',
   schema: booksController.getBookValidation,
   handler: booksController.getBook
},
{
   method: 'POST',
   url: '/api/books',
   handler: booksController.addBook
},
{
   method: 'PUT',
   url: '/api/books/:id',
   handler: booksController.updateBook
},
{
   method: 'DELETE',
   url: '/api/books/:id',
   handler: booksController.deleteBook
}
]
module.exports = routes

Now, let's test the routes. We need to run the index.js file and then we need to hit the URL, http://localhost:3000/api/books/{1} and once we do that it will work as expected. But if you try to pass an object in the id params, like for say if you hit the URL http://localhost:3000/api/books/1, then you will get the following validation error in the browser.

{
   "statusCode": 400,
   "error": "Bad Request",
   "message": "params/id must be object"
}

How to Define Hooks?

We can define hooks in Fastify, by making use of the addHook method.

Consider the example shown below, where we will define a hook that will print all the routes that are registered in our REST API.

index.js addHook

app.addHook('onRoute', (routeOptions) => {
   console.log(`Routes that are registered are: ${routeOptions.url}`)
})

The above code snippet needs to be added to the "index.js" file and then when we run that file, we will get the following output:

Output

Routes that are registered are: /
Routes that are registered are: /
Routes that are registered are: /api/books
Routes that are registered are: /api/books
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books
Routes that are registered are: /api/books/:id
Routes that are registered are: /api/books/:id

Conclusion

In this tutorial, we learned how we can create a REST API using Fastify.

Updated on: 22-Jun-2023

1K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements