Photo by Surja Sen Das Raj on Unsplash
Building a Basic Todo List App with Express.js
Learn Web Dev Fundamentals
It supports the following tasks and endpoints:
Retrieve all todos:
GET /todos
Retrieve a specific todo by ID:
GET /todos/:id
Create a new todo:
POST /todo
Update an existing todo by ID:
PUT /todos/:id
Delete a todo by ID:
DELETE /todos/:id
The todos are stored persistently in a JSON file, and the server uses middleware to parse incoming JSON requests.
Project Setup
First, initialize the project with basic requirements
npm init -y
npm install express
Create a file named todolist.json
to store the todos.
Middleware: Parsing JSON Requests
Configure middleware to parse JSON request bodies.
// Imports
import express from 'express';
import { promises as fs } from 'fs';
const app = express();
const PORT = 3000;
app.use(express.json());
This allows the server to handle incoming JSON data.
File-Based Storage for Persistence
Task: Load Todos from File
// Path to the JSON file
const filePath = 'todolist.json';
let todoArr = [];
async function loadTodosFromFile() {
try {
const data = await fs.readFile(filePath, 'utf-8');
todoArr = JSON.parse(data);
console.log('Todos loaded:', todoArr);
} catch (error) {
console.error('Error loading todos:', error.message);
throw new Error('Something went wrong!');
}
}
// Initialize todos on startup
await loadTodosFromFile()
console.log('Initial todo file: ', todoArr);
This ensures todos are reloaded from todolist.json
when the server starts.
Task: Save Todos to File
async function saveTodosToFile() {
try {
await fs.writeFile(filePath, JSON.stringify(todoArr, null, 2), 'utf-8');
console.log('Todos saved successfully.');
} catch (error) {
console.error('Error saving todos:', error.message);
}
}
This function writes the current state of todos back to todolist.json
.
RESTful Endpoints
Retrieve All Todos
Endpoint: GET /todos
app.get('/todos', (req, res) => {
res.status(200).json(todoArr);
});
Fetches all Todo items.
Retrieve Todo by ID
Endpoint: GET /todos/:id
app.get('/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todo = todoArr.find((t) => t.id === id);
if (!todo) {
return res.status(400).send('Todo not found!');
}
res.status(200).json(todo);
});
Finds a specific Todo by its ID.
Create a New Todo
Endpoint: POST /todo
app.post('/todo', async (req, res) => {
const { title, description } = req.body;
if (!title || !description) {
return res.status(400).send('Title and description are required!');
}
const newTodo = {
id: todoArr.length + 1,
title,
description
};
todoArr.push(newTodo);
await saveTodosToFile();
res.status(200).json({
message: 'Todo added successfully.',
status: 'success',
todo: newTodo
});
});
Creates a new Todo item and assigns it a unique ID.
Update an Existing Todo
Endpoint: PUT /todos/:id
app.put('/todos/:id', async (req, res) => {
const { title, description } = req.body;
const id = parseInt(req.params.id);
const todoIndex = todoArr.findIndex((t) => t.id === id);
if (todoIndex === -1) {
return res.status(400).send('Todo not found!');
}
if (title) todoArr[todoIndex].title = title;
if (description) todoArr[todoIndex].description = description;
await saveTodosToFile();
res.status(200).json({
message: 'Todo updated successfully.',
status: 'success',
todo: todoArr[todoIndex]
});
});
Updates the title or description of an existing Todo.
Delete a Todo
Endpoint: DELETE /todos/:id
app.delete('/todos/:id', async (req, res) => {
const id = parseInt(req.params.id);
const todoIndex = todoArr.findIndex((t) => t.id === id);
if (todoIndex === -1) {
return res.status(400).send('Todo not found!');
}
const deletedTodo = todoArr.splice(todoIndex, 1)[0];
// Reassign IDs after deletion
todoArr = todoArr.map((todo, index) => ({
...todo,
id: index + 1
}));
await saveTodosToFile();
res.status(200).json({
message: 'Todo deleted successfully.',
status: 'success',
todo: deletedTodo
});
});
Deletes a Todo by its ID and reassigns IDs to remaining Todos.
Starting the Server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Starts the server and listens for incoming requests.
Testing
Run the server and test using tools like Postman or curl. Example:
curl http://localhost:3000/todos