In this tutorial, we’ll learn how to build a CRUD Rest API with Fastify. Fastify is a high-performance HTTP framework for Node.js, focusing on speed and inspired by ExpressJS and HapiJS.

Prerequisites:

  • Basic familiarity with JavaScript
  • Node.js installed on the development machine
  • MongoDB installed on the development machine
  • Basic understanding of REST APIs

Step 1: Set up the file structure

Open a terminal and execute the following commands to initialize a new project:

cd desktop
mkdir fastify-service && cd fastify-service
touch index.js
npm init

This creates a new directory on your desktop, navigates into it, creates an index.js file, and initializes a node project using npm.

When running npm init, you’ll be prompted to enter several values, which you can leave blank and update later.

A package.json file will be generated in the root directory, where you can change the values entered during project initialization.

Step 2: Install dependencies

Open the terminal and type the following command to install the required dependencies:

npm i nodemon mongoose fastify boom

The dependencies are:

  • Nodemon: Automatically restarts the server when changes are made to the application
  • Mongoose: An object modeling tool for asynchronously querying MongoDB, providing a schema-based solution to model application data
  • Fastify: A web framework focused on providing the best developer experience with minimal overhead and a robust plugin architecture
  • Boom: A set of utilities for returning HTTP errors for proper error handling.

Step 3: Set up a Fastify server

Add the following code in the index.js file to create a simple Fastify server:

const fastify = require('fastify'); // Import Fastify
const PORT = process.env.PORT || 3000;
const app = fastify({
  logger: true
})
// Declare a route
app.get("/", async () => {
  return {
    Message: "Fastify is On Fire"
  }
})
// Function to run the server
const start = async () => {
  try {
    await app.listen(PORT)
    app.log.info(`server listening on ${app.server.address().port}`)
  } catch (err) {
    app.log.error(err)
    process.exit(1)
  }
}
start();

Here, we require the Fastify framework, declare an instance of Fastify, declare our first route, and initialize the server on port 3000, which is stored in the PORT variable.

Fastify comes with a built-in logger that’s disabled by default. We enable it by adding it to the Fastify instance:

const app = fastify({
  logger: true
})

To run the application, enter the following command in the terminal:

nodemon index.js

Navigate to http://localhost:3000/ and you should see {"Message": "Fastify is On Fire"} returned in the browser.

Step 4: Set up MongoDB

With MongoDB, we don’t need to create a database beforehand; we can specify a name during setup, and MongoDB will create the database once data is stored.

Create a config directory in the project directory, where we will configure Mongoose:

mkdir config && cd config
touch db.js

This creates a config directory, navigates into it, and creates a new file called db.js. Add the following code to the db.js file:

const fastifyPlugin = require('fastify-plugin')
const mongoose = require('mongoose')
// Connect to DB
async function dbConnector(f
astify, options) {
try {
const url = "mongodb://localhost:27017/fastify-blog"
const db = await mongoose
.connect(url, {
useNewUrlParser: true
})
console.log("Database is connected")
fastify.decorate('mongo', db)
} catch (err) {
console.log(err)
}
}
module.exports = fastifyPlugin(dbConnector)

This code connects to the MongoDB database using Mongoose. Now, we need to bring this file as middleware in our application. Middleware is a subset of chained functions called by the Fastify routing layer before the user-defined handler is invoked.

const db = require("./config/db")
app.use(db())

After doing this, you should see “Database is connected” logged in the console, indicating that our application is connected to the database.

Step 5: Create a Model

Create a new folder within the root directory called models, and within it create a new file called Post.js. Add the following code:

const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    content: {
        type: String,
        required: true
    },
    category: {
        type: String,
        required: true
    },
    author: {
        type: String,
        required: true
    }
}, {
    timestamps: true
})
module.exports = mongoose.model('POST', postSchema)

This code creates a Mongoose schema defining the properties of the blog schema. Mongoose converts the schema into a document in the database, and those properties will be converted into fields in the document.

Step 6: Create routes

We will create the following endpoints:

  • HTTP POST /blog —— Create a new blog post
  • HTTP GET /blog —— Get all blog posts
  • HTTP DELETE /blog/:blogId —— Delete a blog post
  • HTTP PUT /blog/:blogId —— Update a blog post

Create a new folder within the root directory called routes, and within it create a new file called postRoutes.js. Add the following code:

const blogController = require('../controller/blogController');
const routes = [{
        method: 'GET',
        url: '/api/posts',
        handler: blogController.getAllPost
    },
    {
        method: 'GET',
        url: '/api/post/:id',
        handler: blogController.getSinglePost
    },
    {
        method: 'POST',
        url: '/api/post',
        handler: blogController.addNewPost,
    },
    {
        method: 'PUT',
        url: '/api/post/:id',
        handler: blogController.updatePost
    },
    {
        method: 'DELETE',
        url: '/api/post/:id',
        handler: blogController.deletePost
    }
]
module.exports = routes

This code requires a blogController file, which we’ll create next. The file will contain methods that handle our routes, separating business logic from the routes.

Step 7: Create a controller

Create a new folder within the root directory called controller, and within it create a new file called blogController.js. Add the following code:

const boom = require('boom')
const Blog = require("../model/Post")
// Get all posts
exports.getAllPost = async (req, reply) => {
    try {
        let posts = await Blog.find();
        return posts;
    } catch (err) {
        throw boom.boomify(err)
    }
}
// Get single post by ID
exports.getSinglePost = async (req, reply) => {
    try {
    const id = req.params.id
    let post = await Blog.findById(id);
    return post
} catch (err) {
    throw boom.boomify(err)
}
}

// Add new post
exports.addNewPost = async (req, reply) => {
try {
let post = new Blog(req.body);
let newpost = await post.save();
return newpost
} catch (err) {
throw boom.boomify(err)
}
}

// Update post
exports.updatePost = async (req, reply) => {
try {
const id = req.params.id
let result = await Blog.findByIdAndUpdate(id, req.body, {
new: true
});
return result
} catch (err) {
throw boom.boomify(err)
}
}

// Delete post
exports.deletePost = async (req, reply) => {
try {
const id = req.params.id
let result = await Blog.findByIdAndDelete(id);
return { Message: "Post Deleted" }
} catch (err) {
throw boom.boomify(err)
}
}

This code requires the Boom package to handle errors. We export each function so it is accessible by our route file. Each function is wrapped in a try-and-catch block, with the catch block using the Boom package to throw errors.

Step 8: Import routes and controller into index.js

Now that we have our routes and controller files, we need to import them into the index.js file.

const routes = require("./routes/postRoutes");

After doing this, add the following code to loop over the route array:

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

Conclusion

Building a CRUD Rest API with Fastify is quite easy. We can use it to develop larger applications like an e-commerce platform. Fastify can be integrated with any frontend framework like Angular, Vue, or React. Understanding the basics is crucial to building successful applications.