DEV Community

suraj kushwaha
suraj kushwaha

Posted on

How to Perform Crud Operation In Express with Mongodb

How to Perform CRUD Operations in Express.js with Mongoose: A Step-by-Step Guide

CRUD—Create, Read, Update, and Delete—are the four fundamental operations that form the backbone of almost every dynamic web application. Mastering them is a critical skill for any backend developer. When building with Node.js, the combination of the Express.js framework for routing and the Mongoose ODM for MongoDB interaction provides a powerful and developer-friendly stack to build robust APIs.

This technical guide will walk you through building a complete RESTful API that performs all four CRUD operations. We will create a simple application to manage blog posts, demonstrating each step with clear code examples and explanations.

Prerequisites

Before we begin, ensure you have the following installed and set up:

  • Node.js and npm: You can download them from the official Node.js website.
  • MongoDB: You need a running MongoDB instance, either locally on your machine or through a cloud service like MongoDB Atlas.
  • A code editor: Such as Visual Studio Code.
  • Basic knowledge: A foundational understanding of JavaScript, Node.js, and how APIs work is recommended.

Step 1: Setting Up the Project Environment

First, let's create our project directory and initialize a new Node.js application.

Initialize the Node.js Project

Open your terminal, create a new folder for your project, and navigate into it.

mkdir express-crud-api
cd express-crud-api
Enter fullscreen mode Exit fullscreen mode

Now, initialize a package.json file using npm.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install Dependencies

We need a few key packages to build our application:

  • express: The web framework for Node.js that will handle our routing and server logic.
  • mongoose: An Object Data Modeling (ODM) library for MongoDB and Node.js. It simplifies interactions with the database.
  • dotenv: A module to load environment variables from a .env file into process.env.
  • nodemon (dev dependency): A tool that automatically restarts the server during development whenever file changes are detected.

Install the required dependencies:

npm install express mongoose dotenv
Enter fullscreen mode Exit fullscreen mode

Install nodemon as a development dependency:

npm install --save-dev nodemon
Enter fullscreen mode Exit fullscreen mode

Next, open your package.json and add a start script to run the server with nodemon.

"scripts": {
  "start": "nodemon server.js"
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating the Express Server and Connecting to MongoDB

Now it's time to write some code. Create a file named server.js in your project's root directory. This file will be the entry point for our application.

Basic Server Setup

Add the following code to server.js to create a minimal Express server.

// server.js
const express = require('express');

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

// Middleware to parse JSON bodies
app.use(express.json());

app.get('/', (req, res) => {
  res.send('CRUD API is running!');
});

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

Database Connection

To keep our database connection string secure, create a .env file in the root directory and add your MongoDB URI.

# .env
MONGO_URI=mongodb://localhost:27017/blogDB
Enter fullscreen mode Exit fullscreen mode

Now, let's update server.js to connect to our MongoDB database using Mongoose. We will also require the dotenv package to load the environment variable.

// server.js
require('dotenv').config(); // Load environment variables
const express = require('express');
const mongoose = require('mongoose');

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

// Middleware
app.use(express.json());

// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI)
  .then(() => {
    console.log('Successfully connected to MongoDB');
    app.listen(PORT, () => {
      console.log(`Server is running on port ${PORT}`);
    });
  })
  .catch((err) => {
    console.error('Database connection error:', err);
  });

app.get('/', (req, res) => {
  res.send('CRUD API is running!');
});
Enter fullscreen mode Exit fullscreen mode

Now, if you run npm start in your terminal, your server should start and connect to the database.

Step 3: Defining the Mongoose Schema and Model

Mongoose works with Schemas and Models. A Schema defines the structure of the documents within a collection, and a Model provides an interface to the database for creating, querying, updating, and deleting records.

Create a new folder named models and inside it, a file named post.model.js.

// models/post.model.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true
  },
  content: {
    type: String,
    required: true
  },
  author: {
    type: String,
    required: false,
    default: 'Anonymous'
  }
}, {
  timestamps: true // Automatically adds createdAt and updatedAt fields
});

const Post = mongoose.model('Post', postSchema);

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

This schema defines a Post with a title, content, and author. The timestamps option will automatically manage createdAt and updatedAt fields for us.

Step 4: Implementing the CRUD Operations

With our model in place, we can now create the API endpoints for each CRUD operation in server.js.

First, import the Post model at the top of your server.js file.

const Post = require('./models/post.model');
Enter fullscreen mode Exit fullscreen mode

1. Create (POST)

To create a new post, we'll define a POST route. This route will expect a JSON object in the request body that matches our postSchema.

// CREATE a new post
app.post('/posts', async (req, res) => {
  try {
    const post = await Post.create(req.body);
    res.status(201).json(post);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode
  • Route: POST /posts
  • Logic: We use an async/await block to handle the asynchronous database operation. Post.create(req.body) creates a new document in the posts collection using the data from the request body.
  • Response: On success, we send a 201 Created status with the newly created post. If an error occurs, we send a 500 Internal Server Error.

2. Read (GET)

Reading data can be done in two ways: fetching a list of all items or fetching a single item by its unique identifier.

Get All Posts

This endpoint will retrieve all posts from the database.

// READ all posts
app.get('/posts', async (req, res) => {
  try {
    const posts = await Post.find({});
    res.status(200).json(posts);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode
  • Route: GET /posts
  • Logic: Post.find({}) queries the collection and returns all documents. An empty object {} as the filter means "match all."
  • Response: We return a 200 OK status with an array of posts.

Get a Single Post by ID

This endpoint will retrieve a specific post using its _id.

// READ a single post by ID
app.get('/posts/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const post = await Post.findById(id);

    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }

    res.status(200).json(post);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode
  • Route: GET /posts/:id (e.g., /posts/60d21b4667d0d8992e610c85)
  • Logic: We extract the id from the URL parameters (req.params). Post.findById(id) finds the document with the matching _id. We also handle the case where no post is found, returning a 404 Not Found.
  • Response: On success, we return the found post.

3. Update (PUT)

To update an existing post, we'll use a PUT request. This route will identify the post by its ID and update it with the data provided in the request body.

// UPDATE a post by ID
app.put('/posts/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const post = await Post.findByIdAndUpdate(id, req.body, { new: true });

    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }

    res.status(200).json(post);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode
  • Route: PUT /posts/:id
  • Logic: Post.findByIdAndUpdate() finds a document by its ID and updates it. The first argument is the id, the second is the update data (req.body), and the third is an options object. { new: true } ensures that the method returns the modified document, not the original one.
  • Response: We return the updated post.

4. Delete (DELETE)

Finally, to delete a post, we'll use the DELETE HTTP method.

// DELETE a post by ID
app.delete('/posts/:id', async (req, res) => {
  try {
    const { id } = req.params;
    const post = await Post.findByIdAndDelete(id);

    if (!post) {
      return res.status(404).json({ message: 'Post not found' });
    }

    res.status(200).json({ message: 'Post deleted successfully' });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode
  • Route: DELETE /posts/:id
  • Logic: Post.findByIdAndDelete() finds a document by its ID and removes it from the collection.
  • Response: On successful deletion, we return a confirmation message.

Conclusion

Congratulations! You have successfully built a complete RESTful API with Express.js and Mongoose, implementing all four essential CRUD operations. You now have a solid foundation for creating, reading, updating, and deleting data from a MongoDB database.

These fundamental patterns are the building blocks for more complex applications. From here, you can expand on this project by adding features like:

  • Input Validation: Use a library like Joi or express-validator to validate incoming data.
  • Authentication and Authorization: Secure your endpoints so only authorized users can perform certain actions.
  • Refactoring: Organize your code by separating routes and controller logic into different files for better maintainability (e.g., using the MVC pattern).

By mastering CRUD operations in Express, you've unlocked the ability to build powerful and scalable backend services for any web or mobile application.

Top comments (0)