Creating Powerful REST APIs: A Step-by-Step Guide to Building and Integrating with Databases using Node.js

Creating Powerful REST APIs: A Step-by-Step Guide to Building and Integrating with Databases using Node.js

Introduction

In today's interconnected digital landscape, building reliable and efficient REST APIs is a core skill for modern developers. Combining the power of a database with a RESTful architecture opens up a world of possibilities for data-driven applications. In this comprehensive guide, we'll walk you through the process of building a simple yet powerful REST API with seamless database integration. Whether you're a beginner or an experienced developer, follow along to learn how to harness the potential of APIs and databases to create impactful apps.

Building RESTful API using Node.js

In this tutorial, we will leverage Express.js, a powerful web application framework for Node.js. Express.js streamlines building, routing, and processing of requests, allowing developers to build server-side applications that are both efficient and easily scalable. Previous API development experience is not a necessary prerequisite. By following the steps outlined, you can build your own RESTful API in just half an hour.

Step 1: API Endpoints

Before diving into the code editor, you should familiarize yourself with the scope of the project and the structure of the API. Our focus includes implementing CRUD operations complemented by seamless database integration, with MongoDB serving as the database solution of choice. For greater clarity, let's establish a precise definition of the relevant endpoints.

HTTP RequestEndpoint nameDescription
GET/usersGet the names of all users present in the database
GET/users/:emailGet a specific user for that email
POST/usersAdd a new user to the database
PUT/users/:emailUpdate details of an already existing user using their email
DELETE/users/:emailDelete details of a user using their email

Having outlined our API endpoints, you may have noticed a common denominator across all of them: the inclusion of /users. You might be wondering whether this uniformity could lead to errors. However, it`s important to clarify that this won't pose an issue. The reason lies in the HTTP request methods – specifically, GET, POST, PUT, and DELETE – which inherently distinguish each request, rendering the endpoints uniquely identifiable and functional.

Step 2: Setting up the project

We need to create a folder that stores our application and set up Express.js in that folder. Open your system terminal and follow the steps :

  1. Create a new directory for your project: mkdir rest-api

  2. Navigate into the project directory: cd rest-api

  3. Initialize new Node.js project: npm init -y

  4. Install Express.js, Mongoose, and DotEnv as dependencies: npm i express mongoose dotenv bcrypt

Step 3: Create the server

Once the project has been initialized, we now need to create the server.

  1. Create a file index.js in your project directory

  2. Open index.js and write the necessary code in it.

const express = require('express');
const app = express();
const mongoose = require('mongoose');
const port = 3000;
require('dotenv').config();

mongoose.connect(process.env.DATABASE_URL)
        .then(()=>{
            app.listen(port,()=>{
                console.log('Databse connected and server listening at port' + port);
            })
        })
        .catch((e)=>{
            console.log(e)
        })

This will import required modules, set up environment variables, and set the port to 3000. This will also create a connection with the database and start the server to listen on port 3000.

Step 4: Set environment variables

Once the server has been created we need to add environment variables so that crucial information such as database connection string (database URL) is not exposed for unauthorized access. For that follow the steps:

  1. Create .env file in your project directory

  2. Copy the connection string for your database from MongoDB.

  3. Add this line to your .env file DATABASE_URL='your_connection_string

  4. Replace your_connection_string with the one provided by MongoDB

Step 5: Define API Routes

  1. Create a folder named router in your project directory

  2. Inside the folder create a file named routes.js

  3. Open the file and define your API routes.

const express = require('express');
const router = express.Router();

// Define your routes here

module.exports = router;

Step 6: Define the Model for the Database

  1. Create a folder named model in your project directory

  2. Inside the folder create a file named user.js

  3. Open the file and define the database schema as well as create a model.

const mongoose = require("mongoose");

// User schema

module.exports = mongoose.model("User", userSchema);

Step 7: Implement User schema

Now, we need to create a User schema in the user.js file so that our database is able to understand how the data is stored and accessed.

const userSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
    },
    password: {
      type: String,
      required: true,
      minlength: 6,
    },
  },
  { versionKey: false }
);

Step 8: Implement CRUD Operations

Once all the above steps are done, we will then implement CRUD operations so that data can be processed.

  1. Import the model we have defined

  2. Import the module bcrypt which will be later used to hash passwords.

const bcrypt = require("bcrypt");
const User = require("../model/User");

Now, we will add all our endpoints. Also, we'll make sure that all of them are async as fetching from the database can sometimes take time.

GET all users /users

router.get("/", async (req, res) => {
  try {
    const users = await User.find();
    res.status(200).json({ users });
  } catch (e) {
    console.error(e);
    res.status(500).json({ message: "Internal server error" });
  }
});

GET users by email

router.get("/:email", async (req, res) => {
  const userEmail = req.params.email;
  try {
    const user = await User.findOne({ email: userEmail });
    if (!user) {
      return res.status(404).json({ message: "User not found" });
    }
    res.status(200).json(user);
  } catch (e) {
    console.error(e);
    res.status(500).json({ message: "Internal server error" });
  }
});

POST a new user

router.post("/", async (req, res) => {
  const { name, email, password } = req.body;

  try {
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      return res
        .status(400)
        .json({ message: "User Already Exists! Login Instead" });
    }
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = new User({ name, email, password: hashedPassword });
    await user.save();
    return res.status(201).json({ user });
  } catch (e) {
    console.error(e);
    res.status(500).json({ message: "Internal Server Error" });
  }
});

PUT Update user by email

router.put("/:email", async (req, res) => {
  const userEmail = req.params.email;
  const { name, email, password } = req.body;

  try {
    const user = await User.findOneAndUpdate(
      { email: userEmail },
      { name, email, password },
      { new: true }
    );
    if (!user) {
      return res.status(404).json({ message: "User not found" });
    }
    return res.status(200).json(user);
  } catch (e) {
    console.error(e);
    return res.status(500).json({ message: "Internal Server Error" });
  }
});

DELETE user by email

router.delete("/:email", async (req, res) => {
  const userEmail = req.params.email;
  try {
    const user = await User.findOneAndDelete({ email: userEmail });
    if (!user) {
      return res.status(404).json({ message: "User not found" });
    }
    return res.status(200).json({ message: "User deleted successfully", user });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ message: "Internal Server Error" });
  }
});

STEP 9: Add route to the server

Though almost everything is complete, our API will still not work because we haven't connected routes to our server. This code shows how we can connect routes to the server.

const apiRouter = require("./router/routes");

app.use(express.json());
app.use("/users", apiRouter);

app.get("/", (req, res) => {
  res.send("root directory. Server running.");
});

We have an app.get so that out / routes show something instead of throwing an error.

STEP 10: Final Code

You can find the final code in Rest API Tutorial Github Repository

And to run the code simply use node index.js in your terminal.

Also, you can access the deployed API here

Conclusion

By the end of this tutorial, you'll have a solid understanding of how to build a simple REST API with database integration. Armed with this knowledge, you'll be well-equipped to expand your skills and develop more complex applications that harness the full potential of APIs and databases. Whether you're building a personal project, a startup, or an enterprise-grade application, the principles and techniques covered in this guide will serve as a solid foundation for your API development journey. your. Get ready to turn ideas into reality and connect the digital world with the power of APIs and databases.