DEV Community

bilal khan
bilal khan

Posted on

The MERN stack series !

Post 4: Building a RESTful API with Express and Node.js

In Post 3, we explored MongoDB and implemented basic CRUD operations. Now, we’ll create a RESTful API with Express and Node.js that serves as the backbone of our MERN stack application, allowing us to manage resources and interact with MongoDB from the client.


What is a RESTful API?

A RESTful API (Representational State Transfer) is an architectural style for building web services. It allows different software systems to communicate over HTTP using standard HTTP methods:

  • GET: Retrieve data from the server.
  • POST: Send new data to the server.
  • PUT: Update existing data on the server.
  • DELETE: Remove data from the server.

In this post, we’ll apply these methods to create endpoints for our User resource.

1. Setting Up the Project

First, make sure you have a project structure with backend and frontend directories, as we set up in Post 2. We’ll add a few new routes to the backend.

Install Dependencies

In the backend folder, ensure you have Express and Mongoose installed. If not, install them with:

npm install express mongoose
Enter fullscreen mode Exit fullscreen mode

2. Building Routes in Express

Express makes it simple to define routes and handle HTTP requests. We’ll create routes for basic CRUD operations.

Organize Routes

Inside backend, create a folder named routes and add a file userRoutes.js to handle user-related routes.

// backend/routes/userRoutes.js
const express = require("express");
const router = express.Router();
const User = require("../models/User");

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

// Get a user by ID
router.get("/:id", async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ message: "User not found" });
    res.json(user);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// Create a new user
router.post("/", async (req, res) => {
  const user = new User(req.body);
  try {
    const newUser = await user.save();
    res.status(201).json(newUser);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// Update a user by ID
router.put("/:id", async (req, res) => {
  try {
    const updatedUser = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
    res.json(updatedUser);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// Delete a user by ID
router.delete("/:id", async (req, res) => {
  try {
    await User.findByIdAndDelete(req.params.id);
    res.json({ message: "User deleted" });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Update the Server Entry File

In index.js, add middleware to handle JSON data and register the userRoutes we just created.

// backend/index.js
const express = require("express");
const mongoose = require("mongoose");
const dotenv = require("dotenv");

dotenv.config();

const app = express();
const PORT = process.env.PORT || 5000;

// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log("Connected to MongoDB"))
  .catch(err => console.error("MongoDB connection error:", err));

app.use(express.json()); // Middleware to parse JSON
app.use("/api/users", require("./routes/userRoutes")); // Register user routes

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Enter fullscreen mode Exit fullscreen mode

3. Testing the API

With the API routes set up, you can test each endpoint using Postman or a similar API client.

  • GET http://localhost:5000/api/users: Retrieves a list of all users.
  • GET http://localhost:5000/api/users/:id: Retrieves a specific user by ID.
  • POST http://localhost:5000/api/users: Creates a new user by sending JSON data (e.g., { "name": "Alice", "email": "alice@example.com", "password": "password" }).
  • PUT http://localhost:5000/api/users/:id: Updates an existing user by sending JSON data.
  • DELETE http://localhost:5000/api/users/:id: Deletes a specific user by ID.

4. Integrating Error Handling and Validation

To make the API more robust, we can add validation and error handling. Here’s a basic example of validating input on user creation.

router.post("/", async (req, res) => {
  const { name, email, password } = req.body;
  if (!name || !email || !password) {
    return res.status(400).json({ message: "All fields are required" });
  }
  try {
    const user = new User({ name, email, password });
    await user.save();
    res.status(201).json(user);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode

Next Steps

In Post 5, we’ll create the frontend UI with React to interact with this API, retrieving and managing user data visually. Our application will start taking shape as we build out the user interface and connect it to the backend API we created here.

Top comments (0)