Building a CRUD Application with Node.js, Express, and MongoDB

RMAG news

In this blog, we will build a simple CRUD (Create, Read, Update, Delete) application using Node.js, Express, and MongoDB. CRUD applications are fundamental in web development and provide a solid foundation for understanding how to interact with databases.

Project Setup

Step 1: Setting Up the Project

First, create a new directory for your project and initialize it with npm:

mkdir crud-nodejs
cd crud-nodejs
npm init -y

Next, install the necessary dependencies:

npm install express mongoose cors dotenv body-parser
npm install –save-dev nodemon

Create the following project structure:

crud-nodejs
├── config
│ └── database.js
├── controllers
│ └── todoController.js
├── middleware
│ └── errorMiddleware.js
├── models
│ └── todo.js
├── routes
│ └── todoRoutes.js
├── .env.example
├── index.js
└── package.json

Step 2: Configuring Environment Variables

Create a .env file (copy from .env.example):

cp .env.example .env

Fill in your MongoDB credentials in the .env file:

PORT=3000
CLIENT_URL=http://localhost:3000
MONGODB_USER=your_mongodb_user
MONGODB_PASSWORD=your_mongodb_password
MONGODB_DBNAME=crud

Step 3: Connecting to MongoDB

In config/database.js, we set up the MongoDB connection using Mongoose:

const mongoose = require(mongoose);
require(dotenv).config();

const dbUser = process.env.MONGODB_USER;
const dbPassword = process.env.MONGODB_PASSWORD;
const dbName = process.env.MONGODB_DBNAME || crud;

const mongoURI = `mongodb+srv://${dbUser}:${dbPassword}@cluster0.re3ha3x.mongodb.net/${dbName}?retryWrites=true&w=majority`;

module.exports = async function connectDB() {
try {
await mongoose.connect(mongoURI, {
useNewUrlParser: true, useUnifiedTopology: true
});
console.log(MongoDB connected);
} catch (error) {
console.error(MongoDB connection failed);
console.error(error);
}
};

Step 4: Creating the Express Server

In index.js, we configure the Express server and connect to MongoDB:

const express = require(express);
const cors = require(cors);
const bodyParser = require(body-parser);
const connectDB = require(./config/database);
const todoRoutes = require(./routes/todoRoutes);
const errorMiddleware = require(./middleware/errorMiddleware);

require(dotenv).config();

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

app.use(
cors({
origin: *,
})
);

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

// Connect to MongoDB
connectDB();

// Routes
app.use(/todos, todoRoutes);

// Error middleware
app.use(errorMiddleware);

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

Step 5: Defining the Todo Model

In models/todo.js, we define the Todo schema using Mongoose:

const mongoose = require(mongoose);

const todoSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
completed: {
type: Boolean,
default: false
}
});

module.exports = mongoose.model(Todo, todoSchema);

Step 6: Creating the Controller

In controllers/todoController.js, we define the logic for handling CRUD operations:

const Todo = require(../models/todo);
const { validateTodo } = require(../utils/validationUtils);

exports.createTodo = async (req, res) => {
const todo = req.body;

if (!validateTodo(todo)) {
return res.status(400).json({ message: Invalid todo object });
}

try {
const newTodo = new Todo(todo);
await newTodo.save();
res.status(201).json({ message: Todo created successfully });
} catch (error) {
res.status(500).json({ message: error.message });
}
};

exports.getAllTodos = async (req, res) => {
try {
const todos = await Todo.find();
res.send(todos);
} catch (err) {
res.status(500).send(err);
}
};

exports.getTodoById = async (req, res) => {
try {
const todo = await Todo.findById(req.params.id);
if (!todo) return res.status(404).send(Todo not found);
res.send(todo);
} catch (err) {
res.status(500).send(err);
}
};

exports.updateTodo = async (req, res) => {
try {
const todo = await Todo.findByIdAndUpdate(req.params.id, req.body, { new: true });
if (!todo) return res.status(404).send(Todo not found);
res.status(200).send({ message: Todo updated successfully});
} catch (err) {
res.status(400).send(err);
}
};

exports.deleteTodo = async (req, res) => {
try {
const todo = await Todo.findByIdAndDelete(req.params.id);
if (!todo) return res.status(404).send(Todo not found);
res.status(200).json({ message: Todo deleted successfully });
} catch (err) {
res.status(500).send(err);
}
};

Step 7: Defining Routes

In routes/todoRoutes.js, we set up the routes for the Todo API:

const express = require(express);
const router = express.Router();
const todoController = require(../controllers/todoController);

// Routes
router.post(/, todoController.createTodo);
router.get(/, todoController.getAllTodos);
router.get(/:id, todoController.getTodoById);
router.patch(/:id, todoController.updateTodo);
router.delete(/:id, todoController.deleteTodo);

module.exports = router;

Step 8: Error Handling Middleware

In middleware/errorMiddleware.js, we define a simple error handling middleware:

module.exports = function errorHandler(err, req, res, next) {
console.error(err.stack);
res.status(500).send(Something broke!);
};

Step 9: Running the Application

Add the following scripts to package.json:

“scripts”: {
“test”: “echo Error: no test specified && exit 1″,
“dev”: “nodemon index.js”,
“start”: “node index.js”
}

Start the application in development mode:

npm run dev

Conclusion

You now have a fully functional CRUD application built with Node.js, Express, and MongoDB. This application allows you to create, read, update, and delete Todo items. This basic structure can be expanded and customized to fit more complex requirements. Happy coding!