Sitemap
Shahpankti

Shahpankti

Step-by-Step MERN Stack CRUD Operations

9 min readMay 11, 2025

--

In this tutorial, we will set up a complete MERN stack application that enables you to perform CRUD (Create, Read, Update, Delete) operations seamlessly. The MERN stack consists of four key technologies: MongoDB, Express.js, React, and Node.js.

  • MongoDB is a NoSQL database that allows us to store and manage data in a flexible, JSON-like format, making it ideal for our application’s needs.
  • Node.js is a JavaScript runtime that enables us to run JavaScript code on the server side, allowing us to build scalable and efficient web applications.
  • Express.js is a web application framework for Node.js that simplifies the process of building APIs and handling requests.
  • React is a popular front-end library for building user interfaces, allowing us to create dynamic and interactive web applications.

In this tutorial, we will first establish a connection with our MongoDB database, ensuring that our application can communicate with the database effectively. Then, we will implement CRUD operations, enabling users to create, view, update, and delete data within the application.

By the end of this tutorial, you will have a solid understanding of how to build a full-stack application using the MERN stack and will be able to create your own CRUD application with confidence.

Step 1: Setting Up Your MERN Stack Project with Vite

To kick off your MERN stack application using Vite, follow these steps to create a new project and set up your development environment:

  1. Create a New Folder
    First, open your terminal or command prompt and create a new directory for your project. You can do this with the following command:
mkdir your-project-name
cd your-project-name

2. Initialize a Vite Project
Next, you’ll initialize a new Vite project by running:

npm init vite

This command will prompt you to select a framework. Choose React (or any other framework you plan to use) when prompted. Vite will then generate the necessary files and folders for your project.

3. Install Dependencies
Once your Vite project is set up, navigate to your project directory (if you haven’t already) and install the required dependencies by running:

npm install

4. Start the Development Server
Finally, to run your application and see it in action, use the following command:

npm run dev

5. Creating the Server

Navigate to the Server Directory

mkdir server 
cd server

6. Initialize Node.js

npm init -y

7. Install Required Packages

npm install express mongoose cors nodemon

8. Create Server Files

  • Create the index.js file:
touch index.js

Create a models directory for MongoDB models:

mkdir models
touch models/Book.js

Step 2: Set Up the Express Server (index.js):

const express = require('express'); // Importing Express
const mongoose = require('mongoose'); // Importing Mongoose for MongoDB
const cors = require('cors'); // Importing CORS to allow cross-origin requests
const bodyParser = require('body-parser'); // Importing body-parser to parse JSON requests
const app = express(); // Creating an instance of Express
const port = 3000; // Defining the port number

app.use(cors()); // Enabling CORS
app.use(bodyParser.json()); // Parsing JSON requests

// Connecting to MongoDB
mongoose.connect('mongodb://localhost:27017/bookDB', {
useNewUrlParser: true,
useUnifiedTopology: true
});

// Starting the server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`); // Logging server start message
});

Step 3: Define the Book Model:

Get Shahpankti’s stories in your inbox

Join Medium for free to get updates from this writer.

Set up Mongoose: Add a Mongoose schema for the books, which will define the structure of our book data.

const mongoose = require('mongoose');

const bookSchema = new mongoose.Schema({
title: String,
author: String,
genre: String,
});

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

Step 4: Create API Endpoints

Add CRUD API endpoints: Implement the following routes in your index.js file:

GET /: Fetch all books

POST /createBook: Create a new book

DELETE /deleteBook/:id: Delete a book by ID

GET /getBook/:id: Fetch a specific book by ID

PUT /updateBook/:id: Update a book by ID

// Importing required modules
const express = require('express'); // Importing Express framework for building the server
const mongoose = require('mongoose'); // Importing Mongoose for MongoDB interactions
const cors = require('cors'); // Importing CORS middleware for cross-origin resource sharing
const BookModel = require('./models/Book'); // Importing the Book model to interact with the 'books' collection

// Creating an instance of the Express application
const app = express();
const port = 3000; // Defining the port number where the server will run

// Using middleware
app.use(cors()); // Enabling CORS for all routes
app.use(express.json()); // Middleware to parse incoming JSON requests

// Connecting to MongoDB database named 'Book'
mongoose.connect('mongodb://127.0.0.1:27017/Book', { useNewUrlParser: true, useUnifiedTopology: true });

// Starting the server and listening on the specified port
app.listen(port, () => {
console.log(`Server is running on port ${port}`); // Logging the server start message
});

// Route to get all books
app.get('/', (req, res) => {
BookModel.find({}) // Fetching all books from the database
.then(books => res.json(books)) // Sending the list of books as a JSON response
.catch(err => res.json(err)); // Handling any errors that occur
});

// Route to create a new book
app.post("/createBook", (req, res) => {
BookModel.create(req.body) // Creating a new book using the data in the request body
.then(book => res.json(book)) // Sending the created book as a JSON response
.catch(err => res.json(err)); // Handling any errors that occur
});

// Route to get a single book by its ID
app.get('/getBook/:id', (req, res) => {
const { id } = req.params; // Extracting the ID from the request parameters
BookModel.findById(id) // Fetching the book with the specified ID
.then(book => res.json(book)) // Sending the book as a JSON response
.catch(err => res.status(404).json(err)); // Sending a 404 error if the book is not found
});

// Route to update a book by its ID
app.put('/updateBook/:id', (req, res) => {
const { id } = req.params; // Extracting the ID from the request parameters
BookModel.findByIdAndUpdate(id, req.body, { new: true }) // Updating the book with the specified ID using the request body
.then(updatedBook => res.json(updatedBook)) // Sending the updated book as a JSON response
.catch(err => res.json(err)); // Handling any errors that occur
});

// Route to delete a book by its ID
app.delete('/deleteBook/:id', (req, res) => {
const { id } = req.params; // Extracting the ID from the request parameters
BookModel.findByIdAndDelete(id) // Deleting the book with the specified ID
.then(deletedBook => res.json(deletedBook)) // Sending the deleted book as a JSON response
.catch(err => res.json(err)); // Handling any errors that occur
});

Step 5: Set Up the Frontend

Create a React app: In the root directory of your project, create a React app:

npx create-react-app client
cd client
npm install axios react-router-dom

Step 6: Create React Components

  1. Books Component(Books.js)
import axios from 'axios'; // Importing Axios for making HTTP requests
import React, { useEffect, useState } from 'react'; // Importing React and necessary hooks
import { Link } from 'react-router-dom'; // Importing Link for routing

function Books() {
const [books, setBooks] = useState([]); // State to hold the list of books

// useEffect runs when the component mounts
useEffect(() => {
// Fetching the list of books from the backend
axios.get('http://localhost:3000')
.then(result => setBooks(result.data)) // Setting the retrieved books to state
.catch(err => console.log(err)); // Handling errors
}, []);

// Function to handle book deletion
const handleDelete = (id) => {
axios.delete(`http://localhost:3000/deleteBook/${id}`) // Sending a DELETE request
.then(res => {
console.log(res); // Logging response for debugging
// Updating state to remove the deleted book
setBooks(books.filter(book => book._id !== id));
})
.catch(err => console.log(err)); // Handling errors
}

return (
<div className='d-flex vh-100 bg-primary justify-content-center align-items-center'>
<div className='bg-white p-5 rounded-3 shadow-lg'>
{/* Link to the Create Book page */}
<Link to="/create" className='btn btn-success'>Add +</Link>
<table className='table'>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Genre</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{/* Mapping over books to display them in table rows */}
{books.map((book) => (
<tr key={book._id}>
<td>{book.title}</td>
<td>{book.author}</td>
<td>{book.genre}</td>
<td>
{/* Link to update book page */}
<Link to={`/update/${book._id}`} className='btn btn-success'>Update</Link>
{/* Button to delete book */}
<button className='btn btn-danger' onClick={() => handleDelete(book._id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}

export default Books; // Exporting the Books component

2. CreateBooks Component(CreateBooks.js)

import React, { useState } from 'react'; // Importing React and useState hook
import axios from 'axios'; // Importing Axios for HTTP requests
import { useNavigate } from 'react-router-dom'; // Importing useNavigate for navigation

function CreateBooks() {
// States to hold form inputs
const [title, setTitle] = useState('');
const [author, setAuthor] = useState('');
const [genre, setGenre] = useState('');
const navigate = useNavigate(); // Hook to navigate between routes

const handleSubmit = (e) => {
e.preventDefault(); // Preventing default form submission

// Sending a POST request to create a new book
axios.post("http://localhost:3000/createBook", { title, author, genre })
.then(result => {
console.log(result); // Logging result for debugging
navigate('/'); // Navigating back to the home page
})
.catch(error => console.log(error)); // Handling errors
};

return (
<div className='d-flex vh-100 bg-primary justify-content-center align-items-center'>
<div className='w-50 bg-white rounded p-3'>
<form onSubmit={handleSubmit}>
<h2>Add Book</h2>
{/* Input fields for title, author, and genre */}
<div className='mb-2'>
<label>Title</label>
<input type="text" placeholder='Enter Title' className='form-control' value={title} onChange={(e) => setTitle(e.target.value)} />
</div>
<div className='mb-2'>
<label>Author</label>
<input type="text" placeholder='Enter Author' className='form-control' value={author} onChange={(e) => setAuthor(e.target.value)} />
</div>
<div className='mb-2'>
<label>Genre</label>
<input type="text" placeholder='Enter Genre' className='form-control' value={genre} onChange={(e) => setGenre(e.target.value)} />
</div>
<button className='btn btn-success'>Submit</button> {/* Submit button */}
</form>
</div>
</div>
);
}

export default CreateBooks; // Exporting the CreateBooks component

3. UpdateBooks Component(UpdateBooks.js)

import React, { useState, useEffect } from 'react'; // Importing React and hooks
import { useParams, useNavigate } from 'react-router-dom'; // Importing hooks for routing
import axios from 'axios'; // Importing Axios for HTTP requests

function UpdateBooks() {
const { id } = useParams(); // Getting the book ID from the URL
const [title, setTitle] = useState(''); // State for title
const [author, setAuthor] = useState(''); // State for author
const [genre, setGenre] = useState(''); // State for genre
const navigate = useNavigate(); // Hook to navigate between routes

// useEffect to fetch the book details when the component mounts
useEffect(() => {
axios.get(`http://localhost:3000/getBook/${id}`) // Fetching book by ID
.then(result => {
setTitle(result.data.title); // Setting the title from the response
setAuthor(result.data.author); // Setting the author from the response
setGenre(result.data.genre); // Setting the genre from the response
})
.catch(err => console.log(err)); // Handling errors
}, [id]);

const handleSubmit = (e) => {
e.preventDefault(); // Preventing default form submission

// Sending a PUT request to update the book
axios.put(`http://localhost:3000/updateBook/${id}`, {
title,
author,
genre
})
.then(() => {
console.log('Book updated successfully'); // Logging success
navigate('/'); // Navigating back to the home page
})
.catch(err => console.log(err)); // Handling errors
};

return (
<div className='d-flex vh-100 bg-primary justify-content-center align-items-center'>
<div className='w-50 bg-white rounded p-3'>
<form onSubmit={handleSubmit}>
<h2>Update Book</h2>
{/* Input fields for title, author, and genre */}
<div className='mb-2'>
<label>Title</label>
<input type="text" placeholder='Enter Title' className='form-control' value={title} onChange={(e) => setTitle(e.target.value)} />
</div>
<div className='mb-2'>
<label>Author</label>
<input type="text" placeholder='Enter Author' className='form-control' value={author} onChange={(e) => setAuthor(e.target.value)} />
</div>
<div className='mb-2'>
<label>Genre</label>
<input type="text" placeholder='Enter Genre' className='form-control' value={genre} onChange={(e) => setGenre(e.target.value)} />
</div>
<button className='btn btn-success'>Submit</button> {/* Submit button */}
</form>
</div>
</div>
);
}

export default UpdateBooks; // Exporting the UpdateBooks component

4. App Component

import React from 'react'; // Importing React
import { BrowserRouter, Routes, Route } from 'react-router-dom'; // Importing routing components
import 'bootstrap/dist/css/bootstrap.min.css'; // Importing Bootstrap for styling
import Books from './Books'; // Importing Books component
import CreateBooks from './CreateBooks'; // Importing CreateBooks component
import UpdateBooks from './UpdateBooks'; // Importing UpdateBooks component

function App() {
return (
<div>
<BrowserRouter> {/* Wrapping app in BrowserRouter for routing */}
<Routes> {/* Defining routes */}
<Route path="/" element={<Books />} /> {/* Route for Books component */}
<Route path="/create" element={<CreateBooks />} /> {/* Route for CreateBooks component */}
<Route path="/update/:id" element={<UpdateBooks />} /> {/* Route for UpdateBooks component with dynamic ID */}
</Routes>
</BrowserRouter>
</div>
);
}

export default App; // Exporting the App component

Add the respective code for each component in the src folder.

Step 8:Run the Application

# Open a terminal for the backend
cd server
npm install # Install backend dependencies
npm start # Start the Node.js server

# Open another terminal for the frontend
cd client
npm install # Install frontend dependencies
npm run dev # Start the React application
Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

Conclusion

In this blog, we explored how to build a simple full-stack application using Node.js, Express, MongoDB, and React. We learned how to set up the backend to handle CRUD operations for book records and create a user-friendly interface for managing those records with React. This project serves as a solid foundation for understanding full-stack development and can be expanded with additional features and functionalities in the future. With these skills, you are now equipped to tackle more complex projects and continue your journey in web development.

--

--