Building a RESTful API with Node.js and Express

In this tutorial, we'll walk through building a simple RESTful API using Node.js and Express. This example demonstrates how our enhanced styling for code blocks and images works.

Prerequisites

Before we begin, make sure you have the following installed:

Setting Up the Project

Let's start by creating a new project directory and initializing it:

The above commands create a new directory for our project, initialize a new npm package, and install Express.

Project Structure

Here's how our project structure will look:

rest-api-example/
  ├── node_modules/
  ├── src/
  │   ├── controllers/
  │   │   └── userController.js
  │   ├── models/
  │   │   └── userModel.js
  │   ├── routes/
  │   │   └── userRoutes.js
  │   └── server.js
  ├── package.json
  └── package-lock.json

Project structure in VS Code

Creating the Server

First, let's create our main server file. Create a file at src/server.js with the following code:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.get('/', (req, res) => {
  res.json({ message: 'Welcome to our API' });
});

// Import user routes
const userRoutes = require('./routes/userRoutes');
app.use('/api/users', userRoutes);

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    error: true,
    message: 'Internal Server Error'
  });
});

// Start server
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

module.exports = app;

Creating the User Model

Next, let's create a simple user model in src/models/userModel.js:

// This is a simple in-memory model for demonstration
// In a real application, you would use a database

const users = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' }
];

class UserModel {
  findAll() {
    return Promise.resolve(users);
  }

  findById(id) {
    const user = users.find(user => user.id === parseInt(id));
    return Promise.resolve(user || null);
  }

  create(userData) {
    const newUser = {
      id: users.length + 1,
      ...userData
    };
    users.push(newUser);
    return Promise.resolve(newUser);
  }

  update(id, userData) {
    const index = users.findIndex(user => user.id === parseInt(id));
    if (index === -1) return Promise.resolve(null);
    
    const updatedUser = { ...users[index], ...userData };
    users[index] = updatedUser;
    return Promise.resolve(updatedUser);
  }

  delete(id) {
    const index = users.findIndex(user => user.id === parseInt(id));
    if (index === -1) return Promise.resolve(false);
    
    users.splice(index, 1);
    return Promise.resolve(true);
  }
}

module.exports = new UserModel();

User Controller

Now, let's create a controller to handle the business logic in src/controllers/userController.js:

const UserModel = require('../models/userModel');

class UserController {
  async getAllUsers(req, res, next) {
    try {
      const users = await UserModel.findAll();
      res.json(users);
    } catch (error) {
      next(error);
    }
  }

  async getUserById(req, res, next) {
    try {
      const user = await UserModel.findById(req.params.id);
      if (!user) {
        return res.status(404).json({ message: 'User not found' });
      }
      res.json(user);
    } catch (error) {
      next(error);
    }
  }

  async createUser(req, res, next) {
    try {
      const { name, email } = req.body;
      
      if (!name || !email) {
        return res.status(400).json({ message: 'Name and email are required' });
      }
      
      const newUser = await UserModel.create({ name, email });
      res.status(201).json(newUser);
    } catch (error) {
      next(error);
    }
  }

  async updateUser(req, res, next) {
    try {
      const updatedUser = await UserModel.update(req.params.id, req.body);
      if (!updatedUser) {
        return res.status(404).json({ message: 'User not found' });
      }
      res.json(updatedUser);
    } catch (error) {
      next(error);
    }
  }

  async deleteUser(req, res, next) {
    try {
      const success = await UserModel.delete(req.params.id);
      if (!success) {
        return res.status(404).json({ message: 'User not found' });
      }
      res.status(204).end();
    } catch (error) {
      next(error);
    }
  }
}

module.exports = new UserController();

Defining Routes

Now let's set up our routes in src/routes/userRoutes.js:

const express = require('express');
const router = express.Router();
const UserController = require('../controllers/userController');

// GET all users
router.get('/', UserController.getAllUsers);

// GET user by ID
router.get('/:id', UserController.getUserById);

// POST new user
router.post('/', UserController.createUser);

// PUT update user
router.put('/:id', UserController.updateUser);

// DELETE user
router.delete('/:id', UserController.deleteUser);

module.exports = router;

Testing the API

Let's test our API with some HTTP requests. You can use tools like Postman, curl, or even a simple fetch in the browser.

Testing API with Postman

Here's a sample curl command to get all users:

Conclusion

We've built a simple RESTful API using Node.js and Express! This example demonstrates:

  1. Setting up an Express server
  2. Creating routes
  3. Implementing controllers
  4. Building a simple data model

In a real-world application, you would want to add:

Next Steps

Try extending this API with:

Happy coding!