Building a REST API with Node.js and Express from Scratch


Introduction
In modern web development, REST APIs serve as the backbone of communication between the frontend and backend, enabling applications to exchange data efficiently. In this tutorial, we will build a REST API using Node.js and Express.js, one of the most popular backend frameworks for JavaScript. This API will perform CRUD (Create, Read, Update, Delete) operations on a user resource, storing data in either MongoDB or PostgreSQL.
By the end of this tutorial, you will have a fully functional API with user authentication using JSON Web Tokens (JWT), and you will also learn how to test and deploy your API.
Setting Up the Development Environment
To get started, ensure you have Node.js installed. If not, download and install it from the official Node.js website.
Once installed, verify the installation:
node -v
npm -v
Creating the Project Structure
Create a new project directory and navigate into it:
mkdir rest-api-node cd rest-api-node
Initialize a new Node.js project:
npm init -y
This command generates a
package.json
file, which stores project metadata and dependencies.
Installing Dependencies
We need several packages to build our API:
npm install express dotenv cors morgan
express: The web framework for building our API
dotenv: Loads environment variables from a
.env
filecors: Enables Cross-Origin Resource Sharing
morgan: Logs API requests for debugging
Creating a Basic Express Server
Create an index.js
file and add the following code:
const express = require("express");
require("dotenv").config();
const app = express();
const PORT = process.env.PORT || 5000;
app.get("/", (req, res) => {
res.send("Welcome to the REST API!");
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Run the server using:
node index.js
Open http://localhost:5000/
in a browser, and you should see "Welcome to the REST API!"
.
Defining the API Structure and Endpoints
A REST API follows standard HTTP methods to interact with resources. Our API will manage users with the following endpoints:
Method | Endpoint | Description |
GET | /api/users | Get all users |
GET | /api/users/:id | Get a single user |
POST | /api/users | Create a new user |
PUT | /api/users/:id | Update a user |
DELETE | /api/users/:id | Delete a user |
Connecting to a Database (MongoDB/PostgreSQL)
We will implement two database options:
Option 1: Using MongoDB with Mongoose
Install MongoDB and the Mongoose ORM:
npm install mongoose
Create a database connection file (config/db.js
):
const mongoose = require("mongoose");
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.once("open", () => console.log("Connected to MongoDB"));
Define a User
model (models/User.js
):
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
name: String,
email: String,
password: String,
});
module.exports = mongoose.model("User", UserSchema);
Option 2: Using PostgreSQL with Sequelize
Install Sequelize and PostgreSQL dependencies:
npm install sequelize pg pg-hstore
Set up the database connection (config/db.js
):
const { Sequelize } = require("sequelize");
const sequelize = new Sequelize(process.env.PG_URI);
sequelize.authenticate()
.then(() => console.log("Connected to PostgreSQL"))
.catch(err => console.log("Error:", err));
module.exports = sequelize;
Define a User
model (models/User.js
):
const { DataTypes } = require("sequelize");
const sequelize = require("../config/db");
const User = sequelize.define("User", {
name: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
});
module.exports = User;
Implementing CRUD Operations in Express
Create a route file (routes/userRoutes.js
):
const express = require("express");
const router = express.Router();
const { getUsers, getUserById, createUser, updateUser, deleteUser } = require("../controllers/userController");
router.get("/", getUsers);
router.get("/:id", getUserById);
router.post("/", createUser);
router.put("/:id", updateUser);
router.delete("/:id", deleteUser);
module.exports = router;
Create a controller file (controllers/userController.js
):
const User = require("../models/User");
exports.getUsers = async (req, res) => {
const users = await User.find();
res.json(users);
};
exports.getUserById = async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user);
};
exports.createUser = async (req, res) => {
const newUser = new User(req.body);
await newUser.save();
res.status(201).json(newUser);
};
exports.updateUser = async (req, res) => {
const updatedUser = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(updatedUser);
};
exports.deleteUser = async (req, res) => {
await User.findByIdAndDelete(req.params.id);
res.json({ message: "User deleted successfully" });
};
Implementing Authentication with JWT
Install dependencies:
npm install jsonwebtoken bcrypt
Update the controller to include user authentication (controllers/authController.js
):
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const User = require("../models/User");
exports.register = async (req, res) => {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
const user = new User({ ...req.body, password: hashedPassword });
await user.save();
res.status(201).json({ message: "User registered successfully" });
};
exports.login = async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (!user || !(await bcrypt.compare(req.body.password, user.password))) {
return res.status(401).json({ message: "Invalid credentials" });
}
const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: "1h" });
res.json({ token });
};
Testing the API with Postman
Send a POST request to
/api/users
with user dataUse a GET request to retrieve all users
Test authentication by making requests with the JWT token
Deploying the API
Host the API on Render, Vercel, or Railway
Deploy to a VPS using PM2 and Nginx
Store environment variables securely
Conclusion
In this tutorial, we built a REST API with Node.js, Express, and MongoDB/PostgreSQL, covering CRUD operations, authentication, and deployment.
To extend this API, consider adding role-based authentication, request validation, and API rate limiting.
Subscribe to my newsletter
Read articles from Victor Uzoagba directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Victor Uzoagba
Victor Uzoagba
I'm a seasoned technical writer specializing in Python programming. With a keen understanding of both the technical and creative aspects of technology, I write compelling and informative content that bridges the gap between complex programming concepts and readers of all levels. Passionate about coding and communication, I deliver insightful articles, tutorials, and documentation that empower developers to harness the full potential of technology.