Building a RESTful API with Express and MongoDB

Building a RESTful API with Express and MongoDB

The MERN stack, which includes MongoDB, Express, React, and Node.js, offers a reliable and quick solution to create modern web apps. In this post, we'll look at how to create a RESTful API with Express and MongoDB, the backend components of the MERN stack. This tutorial will show you how to set up a server, connect to a MongoDB database, and perform CRUD operations.

What are REST API's?

A RESTful API (Representational State Transfer) is a networked application design system that enables stateless communication between clients and servers. It performs CRUD (Create, Read, Update, Delete) actions on resources by using common HTTP methods such as GET, POST, PUT, and DELETE.

What is Express JS?

Express, a minimalist web framework for Node.js, simplifies the creation of robust APIs by providing a range of powerful features.

Initialize the Project

  1. Create a project directory

     mkdir mern-rest-api
     cd mern-rest-api
    
  2. Initialize node.js

npm init -y
  1. Install dependencies

     npm i express mongoose cors
    

Project structure

Your project structure should look like this:

mern-rest-api/
├── node_modules/
├── package.json
├── server.js
└── config/
    └── db.js
└── models/
    └── user.model.js
└── routes/
    └── user.routes.js

Connecting to MongoDB

Inside config folder create a db.js file.

// config/db.js
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect('mongodb://localhost:27017/mernapi');
    console.log('MongoDB connected');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }
};

module.exports = connectDB;

Setting up the express server

In the root directory create a server.js file.

// server.js
const express = require('express');
const connectDB = require('./config/db');
const userRoutes = require('./routes/userRoutes.js');
const app = express();

// Connect Database
connectDB();

// Middleware
app.use(express.json({ extended: false }));
app.use(cors())

app.get('/', (req, res) => res.send('API Running'));

// Define Routes
app.use('/api/items', userRoutes);

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => console.log(`Server started on port http://localhost:${PORT}`));

Creating the model

Inside models folder create a file called user.model.js

// models/user.model.js
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: [true, "Username is required"],
    unique: true,
    index: true,
    minlength: [3, "Username must be at least 3 characters long"],
    maxlength: [30, "Username cannot exceed 30 characters"],
    match: [/^[a-zA-Z0-9_]+$/, "Username can only contain letters, numbers, and underscores"],
  },
  email: {
    type: String,
    required: [true, "Email is required"],
    unique: true,
    index: true,
    match: [/\S+@\S+\.\S+/, "Please enter a valid email address"],
  },
  password: {
    type: String,
    required: [true, "Password is required"],
    minlength: [6, "Password must be at least 6 characters long"],
    maxlength: [128, "Password cannot exceed 128 characters"],
    match: [/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{6,128}$/, "Password must include at least one uppercase letter, one lowercase letter, one number, and one special character"],
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

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

Defining Routes

Inside routes folder create a file called userRoutes.js

// routes/items.js
const express = require('express');
const router = express.Router();

const User = require('../models/User.js');

// @route   GET api/users
// @desc    Get all users
// @access  Public
router.get('/', async (req, res) => {
  try {
    const users = await User.find();
    if (users.length === 0) {
      return res.status(404).json({
        success: false,
        message: 'No users found',
        data: null
      });
    }
    res.status(200).json({
      success: true,
      message: 'Users retrieved successfully',
      data: users
    });
  } catch (err) {
    console.error('Error fetching users:', err.message);
    res.status(500).json({
      success: false,
      message: 'Server Error',
      data: null
    });
  }
});

// @route   POST api/users
// @desc    Create a user
// @access  Public
router.post('/', async (req, res) => {
  const { username, email, password } = req.body;

  // Input validation
  if (!username || !email || !password) {
    return res.status(400).json({
      success: false,
      message: 'Please provide username, email, and password',
      data: null
    });
  }

  try {
    const newUser = await User.create({
      username,
      email,
      password
});
    res.status(201).json({
      success: true,
      message: 'User created successfully',
      data: newUser
    });
  } catch (err) {
    console.error('Error creating user:', err.message);
    res.status(500).json({
      success: false,
      message: 'Server Error',
      data: null
    });
  }
});


// @route   PUT api/users/:id
// @desc    Update a user
// @access  Public
router.put('/:id', async (req, res) => {
  const { username, email, password } = req.body;

  // Input validation
  if (!username && !email && !password) {
    return res.status(400).json({
      success: false,
      message: 'Please provide data to update',
      data: null
    });
  }

  try {
    let user = await User.findById(req.params.id);

    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'User not found',
        data: null
      });
    }

    // Update fields
    if (username) user.username = username;
    if (email) user.email = email;
    if (password) user.password = password;

    await user.save();

    res.json({
      success: true,
      message: 'User updated successfully',
      data: user
    });
  } catch (err) {
    console.error('Error updating user:', err.message);
    res.status(500).json({
      success: false,
      message: 'Server Error',
      data: null
    });
  }
});

// @route   DELETE api/users/:id
// @desc    Delete a user
// @access  Public
router.delete('/:id', async (req, res) => {
  try {
    const user = await User.findById(req.params.id);

    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'User not found',
        data: null
      });
    }

    await user.remove();

    res.json({
      success: true,
      message: 'User removed',
      data: null
    });
  } catch (err) {
    console.error('Error deleting user:', err.message);
    res.status(500).json({
      success: false,
      message: 'Server Error',
      data: null
    });
  }
});

module.exports = router;

Running the server

Now that we have everything set up, you can start the server by running:

   node server.js

Your API should now be running on http://localhost:5000, and you can test the various endpoints using tools like Postman, Insomnia or curl.


If you found this post helpful, please like it and leave a comment with your ideas and questions.

Happy coding👋