Creating CRUD APIs with Node.js and MongoDB

In this tutorial, we will learn how to build a RESTful API using Node.js and MongoDB to perform CRUD operations. CRUD operations are essential for interacting with databases and are fundamental to web development.

Prerequisites

Before we begin, ensure you have the following installed on your system:

  • Node.js (with npm or yarn)
  • MongoDB (Community Edition)

Step 1: Setting Up Your Environment

Initialize a Node.js Project
    • Create a new directory for your project.
    • Open a terminal or command prompt and navigate to your project directory.
    mkdir node-mongodb-crud-api
    cd node-mongodb-crud-api
    • Initialize a new Node.js project using npm.
    npm init -y

    Install necessary packages:

    • Express: For building web APIs.
    • Mongoose: MongoDB object modeling tool.
    npm install express mongoose
    Create a MongoDB Database
    • Ensure MongoDB is running on your local machine or provide connection details for a remote MongoDB server.
    Step 2: Define the MongoDB Schema

    Create a Mongoose Schema

    • Define the structure of our MongoDB documents using Mongoose Schema in a new file models/Book.js.
    // models/Book.js
    const mongoose = require('mongoose');
    
    const bookSchema = new mongoose.Schema({
        title: { type: String, required: true },
        author: { type: String, required: true },
        published_date: { type: Date, default: Date.now },
        pages: { type: Number, required: true },
        language: { type: String, required: true }
    });
    
    module.exports = mongoose.model('Book', bookSchema);
    
    Step 3: Implementing CRUD Operations

    Create Operation (POST Method)

    • Implement a route to create new books in MongoDB in routes/books.js.
    // routes/books.js
    const express = require('express');
    const router = express.Router();
    const Book = require('../models/Book');
    
    // POST /books - Create a new book
    router.post('/', async (req, res) => {
        try {
            const book = await Book.create(req.body);
            res.status(201).json(book);
        } catch (err) {
            res.status(400).json({ message: err.message });
        }
    });
    
    module.exports = router;

    Read Operation (GET Method)

    • Implement routes to retrieve books from MongoDB.
    // routes/books.js
    // GET /books - Retrieve all books
    router.get('/', async (req, res) => {
        try {
            const books = await Book.find();
            res.json(books);
        } catch (err) {
            res.status(500).json({ message: err.message });
        }
    });
    
    // GET /books/:id - Retrieve a specific book by ID
    router.get('/:id', getBook, (req, res) => {
        res.json(res.book);
    });
    
    async function getBook(req, res, next) {
        try {
            const book = await Book.findById(req.params.id);
            if (book == null) {
                return res.status(404).json({ message: 'Cannot find book' });
            }
            res.book = book;
            next();
        } catch (err) {
            return res.status(500).json({ message: err.message });
        }
    }
    Update Operation (PUT/PATCH Methods)
    • Implement routes to update existing books in MongoDB.
    // routes/books.js
    // PUT /books/:id - Update a book by ID
    router.put('/:id', getBook, async (req, res) => {
        if (req.body.title != null) {
            res.book.title = req.body.title;
        }
        if (req.body.author != null) {
            res.book.author = req.body.author;
        }
        if (req.body.published_date != null) {
            res.book.published_date = req.body.published_date;
        }
        if (req.body.pages != null) {
            res.book.pages = req.body.pages;
        }
        if (req.body.language != null) {
            res.book.language = req.body.language;
        }
        try {
            const updatedBook = await res.book.save();
            res.json(updatedBook);
        } catch (err) {
            res.status(400).json({ message: err.message });
        }
    });
    Delete Operation (DELETE Method)
    • Implement routes to delete books from MongoDB.
    // routes/books.js
    // DELETE /books/:id - Delete a book by ID
    router.delete('/:id', getBook, async (req, res) => {
        try {
            await res.book.remove();
            res.json({ message: 'Deleted book' });
        } catch (err) {
            res.status(500).json({ message: err.message });
        }
    });
    Step 4: Error Handling and Validation

    Error Handling Middleware

    • Implement middleware to handle errors in app.js`.
    // app.js
    const express = require('express');
    const mongoose = require('mongoose');
    const app = express();
    const booksRouter = require('./routes/books');
    
    app.use(express.json());
    
    mongoose.connect('mongodb://localhost:27017/node-mongodb-crud-api', {
        useNewUrlParser: true,
        useUnifiedTopology: true
    });
    const db = mongoose.connection;
    db.on('error', (err) => console.error('MongoDB connection error:', err));
    db.once('open', () => console.log('Connected to MongoDB'));
    
    app.use('/books', booksRouter);
    
    app.listen(3000, () => console.log('Server started on http://localhost:3000'));
    

    Data Validation

    • Use Mongoose schema validation for required fields and data types in models/Book.js`.
    // models/Book.js
    const bookSchema = new mongoose.Schema({
        title: { type: String, required: true },
        author: { type: String, required: true },
        published_date: { type: Date, default: Date.now },
        pages: { type: Number, required: true },
        language: { type: String, required: true }
    });
    Step 5: Testing Your APIs

    Use Postman or Insomnia

    • Test each CRUD operation using API testing tools to verify functionality.

    Additional Resources

    RSS
    Follow by Email
    LinkedIn
    Share
    Scroll to Top