To-Do-List With MongoDB Integrated
Last Updated :
08 Oct, 2025
This project is a full-stack CRUD (Create–Read–Update–Delete) web app that manages tasks with a simple, responsive UI. The backend uses Node.js + Express for routing and middleware, EJS for server-side templating, and MongoDB (via Mongoose) for persistence.
File and Folder Organization
Use this minimal structure to separate concerns—routes, views, models, and static assets so the app stays easy to navigate, extend, and maintain.
Code Base
App.js: It boots the Express server, connects MongoDB via Mongoose, defines Item/List schemas and routes, renders EJS views, and handles adding/deleting tasks (including auto-created custom lists).
app.js
//jshint esversion:6
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const date = require(__dirname + "/date.js");
const _ = require("lodash");
const app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
mongoose.connect("mongodb://localhost:27017/todolistDB")
const itemsSchema = {
name: String
};
const Item = mongoose.model("Item", itemsSchema);
const item1 = new Item({
name: "Welcome to your todolist!"
});
const item2 = new Item({
name: "Hit the + button to add a new item."
});
const item3 = new Item({
name: "<-- Hit this to delete an item."
});
const defaultItems = [item1, item2, item3];
const listSchema = {
name: String,
items: [itemsSchema]
};
const List = mongoose.model("List", listSchema);
app.get("/", function (req, res) {
Item.find().then(foundItems => {
// console.log(foundItems);
if (foundItems.length === 0) {
Item.insertMany(defaultItems)
.then(results => {
console.log('Documents inserted successfully');
})
.catch(err => {
console.error(err);
});
res.redirect("/");
}
else {
res.render("list", { listTitle: "Today", newListItems: foundItems });
}
})
.catch(err => {
console.error(err);
});
});
app.get("/:customListName", function (req, res) {
const customListName = _.capitalize(req.params.customListName);
List.findOne({ name: customListName })
.then(foundList => {
if (!foundList)
{
// Create a new list
const list = new List({
name: customListName,
items: defaultItems
});
list.save();
res.redirect("/" + customListName);
}
else
{
// show an existing list
res.render("list", { listTitle: foundList.name, newListItems: foundList.items });
}
})
.catch(err => {
console.error(err);
});
});
app.post("/", function (req, res) {
const itemName = req.body.newItem;
const listName = req.body.list;
const item = new Item({
name: itemName
});
if (listName === "Today") {
item.save();
res.redirect("/");
}
else {
List.findOne({ name: listName })
.then(foundList => {
foundList.items.push(item);
foundList.save();
res.redirect("/" + listName);
})
.catch(err => {
console.error(err);
});
}
});
app.post("/delete", function (req, res) {
const checkedItemId = req.body.checkbox;
const listName = req.body.listName;
if (listName === "Today") {
Item.findByIdAndRemove(checkedItemId)
.then(() => {
console.log("succesfully deleted checked Item.");
})
.catch(err => {
console.error(err);
});
res.redirect("/");
}
else {
List.findOneAndUpdate({ name: listName }, {
$pull: {
items: {
_id: checkedItemId
}
}
})
.then(foundList => {
if (foundList) {
res.redirect("/" + listName);
} else {
console.log("Error updating list");
}
})
.catch(err => {
console.error(err);
});
}
});
app.get("/about", function (req, res) {
res.render("about");
});
app.listen(3000, function () {
console.log("Server started on port 3000");
});
Date.js: It provides two helpers—getDate() and getDay()—that return nicely formatted, locale-based date/weekday strings using toLocaleDateString().
date.js
//jshint esversion:6
exports.getDate = function() {
const today = new Date();
const options = {
weekday: "long",
day: "numeric",
month: "long"
};
return today.toLocaleDateString("en-US", options);
};
exports.getDay = function () {
const today = new Date();
const options = {
weekday: "long"
};
return today.toLocaleDateString("en-US", options);
};
Views: Reusable EJS templates that render dynamic HTML from server data, composing pages with shared partials (header/footer) and list-specific content.
about.ejs
<%- include("header") -%>
<h2>This is the about page</h2>
<p>jkhkjshjks dhfjkh sjkdfh sjkdf jksdfhjksfhjks dhjfk sjkdfjksdfhjkshdfjk sdjkf sjkdf jksd fjks djfkhsjkdf hsjkd fhs</p>
<%- include("footer") -%>
footer.ejs
</body>
<footer>
Copyright Bhaskar Kulshrestha
</footer>
</html>
header.ejs
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>To Do List</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
list.ejs
<%- include("header") -%>
<div class="box" id="heading">
<h1>
<%= listTitle %>
</h1>
</div>
<div class="box">
<% newListItems.forEach(function(item){ %>
<form action="/delete" method="post">
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onChange="this.form.submit()">
<p>
<%=item.name%>
</p>
</div>
<input type="hidden" name="listName" value="<%= listTitle %>"></input>
</form>
<% }) %>
<form class="item" action="/" method="post">
<input type="text" name="newItem" placeholder="New Item" autocomplete="off">
<button type="submit" name="list" value="<%= listTitle %>">+</button>
</form>
</div>
<%- include("footer") -%>
CSS: This CSS creates a clean, card-style to-do UI with a purple diagonal gradient background, centered container, checkable list items with strike-through on completion, and minimal, accessible form controls.
CSS
html {
background-color: #E4E9FD;
background-image: -webkit-linear-gradient(65deg, #A683E3 50%, #E4E9FD 50%);
min-height: 1000px;
font-family: 'helvetica neue';
}
h1 {
color: #fff;
padding: 10px;
}
.box {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 5px 5px 15px -5px rgba(0, 0, 0, 0.3);
}
#heading {
background-color: #A683E3;
text-align: center;
}
.item {
min-height: 70px;
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked+p {
text-decoration: line-through;
text-decoration-color: #A683E3;
}
input[type="checkbox"] {
margin: 20px;
}
p {
margin: 0;
padding: 20px;
font-size: 20px;
font-weight: 200;
color: #00204a;
}
form.item {
text-align: center;
margin-left: 20px;
}
button {
min-height: 50px;
width: 50px;
border-radius: 50%;
border-color: transparent;
background-color: #A683E3;
color: #fff;
font-size: 30px;
padding-bottom: 6px;
border-width: 0;
}
input[type="text"] {
text-align: center;
height: 60px;
top: 10px;
border: none;
background: transparent;
font-size: 20px;
font-weight: 200;
width: 313px;
}
input[type="text"]:focus {
outline: none;
box-shadow: inset 0 -3px 0 0 #A683E3;
}
::placeholder {
color: grey;
opacity: 1;
}
footer {
color: white;
color: rgba(0, 0, 0, 0.5);
text-align: center;
}
Output
Core Files Explained
Entry Point
App.js: The main server file that connects all parts of the project.
- Initializes the Express app and middleware.
- Connects to MongoDB using Mongoose.
- Defines the routes for displaying, adding, and deleting tasks.
- Renders dynamic EJS templates for the frontend.
Date Utility
date.js: Provides date formatting helper functions.
- getDate(): Returns the full date (e.g., Tuesday, October 7).
- getDay(): Returns only the weekday (e.g., Tuesday).
Database (MongoDB + Mongoose)
Schemas and Models
- Item: Represents a single to-do task (name field).
- List: Represents a custom list (with name and an array of items).
Database
- Connected via mongoose.connect("mongodb://localhost:27017/todolistDB").
- Default tasks are created if the collection is empty.
Views (EJS Templates)
header.ejs
- Defines the HTML structure and links the main CSS file.
- Used as a common header for all pages.
Views (EJS Templates)
header.ejs
- Defines the HTML structure and links the main CSS file.
- Used as a common header for all pages.
list.ejs
- Displays the list title and all tasks (
newListItems). - Includes forms to add and delete items.
- Dynamically updates based on user actions or custom URLs (e.g.,
/Work).
footer.ejs
- Common footer for all pages with copyright info.
about.ejs
- Simple static About page describing the app or author.
Public (Static Files)
public/css/style.css: Defines the UI styling.
- Handles layout, typography, buttons, and gradients.
- Provides a clean card-style design for the to-do box.
Routes (Express)
- GET “/”: Displays the main “Today” list. Inserts default tasks if none exist.
- GET “/:customListName” Handles custom routes like /Work or /Home. Creates new custom lists automatically if they don’t exist.
- POST “/”: Adds a new item to either the “Today” list or a custom list.
- POST “/delete”: Deletes the selected item from the appropriate list.
- GET “/about”: Renders the About page.
Explore
MongoDB Tutorial
7 min read
Introduction
Installation
Basics of MongoDB
MongoDB Methods
Comparison Operators
Logical Operators
Arithmetic Operators
Field Update Operators
Array Expression Operators