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 postHTTP GET /blog
—— Get all blog postsHTTP DELETE /blog/:blogId
—— Delete a blog postHTTP 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.