Skip to content

Building a RESTful API with Node.js and Express: A Step-by-Step Guide

Building a RESTful API with Node.js and Express: A Step-by-Step Guide

Creating a RESTful API in Node.js with Express is ideal for building scalable and efficient backends. A RESTful API allows clients to interact with your application via HTTP methods such as GET, POST, PUT, and DELETE. This guide walks you through setting up a RESTful API with Express, handling CRUD operations, connecting to MongoDB, and structuring the project for scalability.


What is a RESTful API?

A RESTful API is an architectural style for designing networked applications. It relies on HTTP methods and follows resource-oriented principles:

  • GET: Retrieve data.
  • POST: Create new data.
  • PUT: Update existing data.
  • DELETE: Remove data.

REST APIs are stateless, meaning each request from a client contains all the information needed to process it.


Setting Up the Project

If you’re starting a new project, initialize it with npm and install Express and Mongoose for database interactions.

Step 1: Initialize the Project and Install Dependencies

# @filename: script.sh
mkdir restful-api
cd restful-api
npm init -y
npm install express mongoose dotenv
  • express: Web framework for handling HTTP requests.
  • mongoose: ODM (Object Data Modeling) library for MongoDB, providing schema-based data validation.
  • dotenv: For managing environment variables.

Step 2: Configure Environment Variables

Create a .env file to store configuration variables, such as the MongoDB connection URI.

.env

PORT=5000
MONGODB_URI=mongodb://localhost:27017/restful_api

Note: Add .env to your .gitignore file to keep sensitive information secure.


Setting Up Express and Connecting to MongoDB

Create an entry point, server.js, to initialize Express, connect to MongoDB, and define your routes.

server.js

// @filename: server.js
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')

const app = express()
const port = process.env.PORT || 3000

// Middleware
app.use(express.json()) // Parse JSON bodies

// MongoDB Connection
mongoose
  .connect(process.env.MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => console.log('Connected to MongoDB'))
  .catch((error) => console.error('MongoDB connection error:', error))

// Basic route for testing
app.get('/', (req, res) => {
  res.send('Welcome to the RESTful API')
})

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

In this setup:

  • express.json() middleware parses incoming JSON data.
  • mongoose.connect() connects to MongoDB using the URI from .env.

Designing a Sample Resource: Books

Let’s create a resource for managing books. Each book will have a title, author, and year of publication.

Step 1: Define the Book Schema

In a models folder, create a Book.js file to define a Mongoose schema for books.

models/Book.js

// @filename: config.js
const mongoose = require('mongoose')

const bookSchema = new mongoose.Schema({
  title: { type: String, required: true },
  author: { type: String, required: true },
  year: { type: Number, required: true },
})

module.exports = mongoose.model('Book', bookSchema)

This schema requires each book to have a title, author, and year.


Setting Up CRUD Routes

Create a routes folder with a books.js file to handle CRUD operations for books.

routes/books.js

// @filename: routes.js
const express = require('express')
const Book = require('../models/Book')

const router = express.Router()

// Create a new book
router.post('/', async (req, res) => {
  try {
    const book = new Book(req.body)
    const savedBook = await book.save()
    res.status(201).json(savedBook)
  } catch (error) {
    res.status(400).json({ message: error.message })
  }
})

// Get all books
router.get('/', async (req, res) => {
  try {
    const books = await Book.find()
    res.status(200).json(books)
  } catch (error) {
    res.status(500).json({ message: error.message })
  }
})

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

// Update a book by ID
router.put('/:id', async (req, res) => {
  try {
    const updatedBook = await Book.findByIdAndUpdate(req.params.id, req.body, {
      new: true,
    })
    if (!updatedBook) return res.status(404).json({ message: 'Book not found' })
    res.status(200).json(updatedBook)
  } catch (error) {
    res.status(400).json({ message: error.message })
  }
})

// Delete a book by ID
router.delete('/:id', async (req, res) => {
  try {
    const deletedBook = await Book.findByIdAndDelete(req.params.id)
    if (!deletedBook) return res.status(404).json({ message: 'Book not found' })
    res.status(200).json({ message: 'Book deleted' })
  } catch (error) {
    res.status(500).json({ message: error.message })
  }
})

module.exports = router

In this file:

  1. POST /books: Creates a new book.
  2. GET /books: Retrieves all books.
  3. GET /books/:id: Retrieves a single book by ID.
  4. PUT /books/:id: Updates a book by ID.
  5. DELETE /books/:id: Deletes a book by ID.

Step 2: Integrate Routes with Express

Add the books route to your Express server in server.js.

server.js

// @filename: server.js
const express = require('express')
const mongoose = require('mongoose')
const bookRoutes = require('./routes/books')

const app = express()
const port = process.env.PORT || 3000

app.use(express.json())
app.use('/api/books', bookRoutes) // Route for books

app.listen(port, () => {
  console.log(`Server running on port ${port}`)
})

Testing the API with Postman

  1. Create a Book: Send a POST request to http://localhost:5000/api/books with JSON data like:

    {
      "title": "The Great Gatsby",
      "author": "F. Scott Fitzgerald",
      "year": 1925
    }
  2. Retrieve All Books: Send a GET request to http://localhost:5000/api/books to get a list of all books.

  3. Retrieve a Single Book: Send a GET request to http://localhost:5000/api/books/:id, replacing :id with a valid book ID.

  4. Update a Book: Send a PUT request to http://localhost:5000/api/books/:id with updated data.

  5. Delete a Book: Send a DELETE request to http://localhost:5000/api/books/:id.


Structuring the Project for Scalability

As your application grows, organizing code into folders for models, routes, controllers, and services can improve maintainability and scalability.

Example Folder Structure

restful-api/
├── config/            # Database configuration
├── controllers/       # Controller files for handling business logic
├── models/            # Mongoose models
├── routes/            # Route definitions
├── services/          # Services for handling reusable logic
├── .env               # Environment variables
├── .gitignore         # Git ignore file
├── server.js          # Entry point

By separating concerns, you make it easier to add new features and manage complex applications.


Adding Basic Error Handling Middleware

To improve the API’s resilience, create middleware for handling errors.

middleware/errorHandler.js

// @filename: config.js
const errorHandler = (err, req, res, next) => {
  console.error(err.stack)
  res.status(500).json({ message: 'Something went wrong!' })
}

module.exports = errorHandler

Add this middleware to server.js to catch errors globally.

server.js

const errorHandler = require('./middleware/errorHandler')
app.use(errorHandler)

Conclusion

Building a RESTful API with Node.js and Express involves setting up routes, managing data with MongoDB, and structuring the project for scalability. By following these steps, you can create a robust and maintainable API

that supports CRUD operations and adheres to REST principles. Integrate these techniques into your project to build efficient, reliable backends for your applications.

Node.js JavaScript Backend Express REST API MongoDB
Share:

Continue Reading

Implementing File Uploads in Node.js with Multer and Express

Handling file uploads in a Node.js application allows users to share images, documents, and other media files directly with your server. Using Multer with Express, you can configure a robust file upload system that securely manages files, validates their types, and stores them effectively. This guide will walk you through setting up file uploads, covering everything from configuring storage to securing and validating uploaded files.

Read article
Node.jsJavaScriptBackend

Understanding and Implementing CORS in Node.js and Express

CORS (Cross-Origin Resource Sharing) is a security feature in web browsers that restricts web pages from making requests to a different domain than the one that served the original page. By configuring CORS in a Node.js and Express application, you can control which origins are allowed to interact with your server. This guide explains the concept of CORS, why it’s essential, and how to configure it in Express.

Read article
Node.jsJavaScriptBackend

Building a REST API with Node.js and MongoDB: A Step-by-Step Guide

Building a REST API is essential for modern web applications, enabling you to interact with your backend using standard HTTP methods. In this guide, we will build a REST API with Node.js, Express, and MongoDB, covering everything from setting up the project to implementing CRUD operations. By the end, you will have a fully functional API that you can use as a backend for web or mobile applications.

Read article
Node.jsMongoDBREST API