Loading

Quipoin Menu

Learn • Practice • Grow

express-js / CRUD Operations with Database
tutorial

CRUD Operations with Database

Imagine you're running a library. Every day, you add new books (Create), find books for readers (Read), update book information (Update), and remove old books (Delete). These four operations are the foundation of any application that works with data. In web development, they're called CRUD operations.

What is CRUD?

CRUD stands for the four basic operations of persistent storage:
  • Create Add new records to the database
  • Read Retrieve existing records
  • Update Modify existing records
  • Delete Remove records

CRUD in Express APIs

In RESTful APIs, CRUD operations typically map to HTTP methods:
  • POST Create
  • GET Read
  • PUT / PATCH Update
  • DELETE Delete

Complete CRUD Example with MongoDB/Mongoose
Let's build a complete task management API using MongoDB and Mongoose.

1. Setup and Model
const express = require('express');
const mongoose = require('mongoose');
const app = express();

app.use(express.json());

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/taskdb')
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Connection error:', err));

// Define Task schema
const taskSchema = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String },
completed: { type: Boolean, default: false },
priority: { type: String, enum: ['low', 'medium', 'high'], default: 'medium' },
dueDate: { type: Date },
createdAt: { type: Date, default: Date.now }
});

const Task = mongoose.model('Task', taskSchema);

2. Create (POST) Add a new task
app.post('/api/tasks', async (req, res) => {
try {
const task = new Task(req.body);
await task.save();
res.status(201).json({
success: true,
data: task,
message: 'Task created successfully'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});

3. Read (GET) Retrieve tasks
// Get all tasks (with optional filtering)
app.get('/api/tasks', async (req, res) => {
try {
const { completed, priority } = req.query;
const filter = {};
if (completed !== undefined) filter.completed = completed === 'true';
if (priority) filter.priority = priority;

const tasks = await Task.find(filter).sort({ createdAt: -1 });
res.json({
success: true,
count: tasks.length,
data: tasks
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});

// Get single task by ID
app.get('/api/tasks/:id', async (req, res) => {
try {
const task = await Task.findById(req.params.id);
if (!task) {
return res.status(404).json({
success: false,
error: 'Task not found'
});
}
res.json({
success: true,
data: task
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});

4. Update (PUT) Update an existing task
app.put('/api/tasks/:id', async (req, res) => {
try {
const task = await Task.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
);
if (!task) {
return res.status(404).json({
success: false,
error: 'Task not found'
});
}
res.json({
success: true,
data: task,
message: 'Task updated successfully'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});

// PATCH for partial updates
app.patch('/api/tasks/:id', async (req, res) => {
try {
const task = await Task.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true, runValidators: true }
);
if (!task) {
return res.status(404).json({
success: false,
error: 'Task not found'
});
}
res.json({
success: true,
data: task,
message: 'Task updated partially'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});

5. Delete (DELETE) Remove a task
app.delete('/api/tasks/:id', async (req, res) => {
try {
const task = await Task.findByIdAndDelete(req.params.id);
if (!task) {
return res.status(404).json({
success: false,
error: 'Task not found'
});
}
res.json({
success: true,
message: 'Task deleted successfully'
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});

6. Additional Operations
Sometimes you need more than basic CRUD:
// Bulk create
app.post('/api/tasks/bulk', async (req, res) => {
try {
const tasks = await Task.insertMany(req.body.tasks);
res.status(201).json({
success: true,
count: tasks.length,
data: tasks
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});

// Delete all completed tasks
app.delete('/api/tasks/completed/clear', async (req, res) => {
try {
const result = await Task.deleteMany({ completed: true });
res.json({
success: true,
deletedCount: result.deletedCount,
message: 'Completed tasks cleared'
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});

Error Handling Best Practices
  • Always use try/catch in async route handlers.
  • Return appropriate HTTP status codes (200, 201, 400, 404, 500).
  • Send consistent error response format.
  • Validate input before database operations.
  • Check if documents exist before updating/deleting.

Two Minute Drill
  • CRUD = Create, Read, Update, Delete the four essential database operations.
  • Map to HTTP methods: POST (Create), GET (Read), PUT/PATCH (Update), DELETE (Delete).
  • Use Mongoose methods: save(), find(), findById(), findByIdAndUpdate(), findByIdAndDelete().
  • Always validate input and handle errors gracefully.
  • Return consistent JSON responses with success flags and data/error messages.
  • Support filtering, sorting, and pagination for Read operations.

Need more clarification?

Drop us an email at career@quipoinfotech.com