Introduction
In this blog, we'll explore how to build a simple Todo API using Express and TypeScript. The project will allow users to create, retrieve, update, and fetch todos while maintaining them in memory using a Map
. This is a great way to learn how to handle API requests and responses in a structured way.
Setting Up the Project
Before starting, please make sure you have Node.js and Bun installed as the package manager. You can get Bun from Bun's official website if you haven't installed Bun yet.
Initialize the project:
bun init
Install the necessary dependencies:
bun add express
Install TypeScript and type definitions:
bun add -d typescript @types/express
Creating the Express Server
We start by importing Express and defining the Todo
interface and a Map to store todos.
import express from "express";
import { Status } from "./enum";
const app = express();
app.use(express.json());
interface Todo {
title: string;
description: string;
status: boolean;
}
let todos = new Map<string, Todo>();
Function to Store Data
To validate and store a new todo, we create a helper function setData
.
function setData(id: string, todo: Todo) {
if (!todo.title || !todo.description || todo.status === undefined) {
return false;
}
todos.set(id, todo);
return true;
}
API Endpoints
Root Endpoint
This is a simple endpoint that returns a welcome message.
app.get("/", (_req, res) => {
res.json({ message: "Hello, World" });
});
Create a Todo
Users can create a new todo by sending a POST
request with a title, description, and status.
app.post("/create-todo", (req, res) => {
const { title, description, status }: Todo = req.body;
const id: string = crypto.randomUUID();;
const isAdded: boolean = setData(id, { title, description, status });
if (!isAdded) {
res.status(Status.BadRequest).send("error in body");
return;
}
res.status(Status.Ok).send("todo created");
});
Retrieve All Todos
Fetches all stored todos.
app.get("/get-todos", (_req, res) => {
if (todos.size == 0) {
res.status(Status.NotFounded).json({ message: "no todos in database" });
return;
}
res.status(Status.Ok).json(Object.fromEntries(todos));
});
Retrieve a Single Todo by ID
Fetches a todo by its ID.
app.get("/get-todo/:id", (req, res) => {
const id: string = req.params.id;
const todo = todos.get(id);
if (!todo) {
res.status(Status.NotFounded).json({ message: `no todo found with ID: ${id}` });
return;
}
res.status(Status.Ok).json(todo);
});
Update a Todo's Status
Users can update a todo's status using a PATCH
request.
app.patch("/update-todo/:id", (req, res) => {
const id = req.params.id;
const { status }: { status: boolean } = req.body;
let todo = todos.get(id);
if (!todo) {
res.status(Status.NotFounded).json({ message: "todo not found" });
return;
}
todo.status = status;
todos.set(id, todo);
res.status(Status.Ok).json({ message: "todo updated", todo });
});
Status Enum
For better readability and maintainability, we define an enum
for HTTP status codes.
export enum Status {
Ok = 200,
NoContent = 204,
BadRequest = 400,
NotFounded = 404,
}
Running the Server
Start the server using:
bun run index.ts
You should see:
server is serving on http://localhost:3000
Conclusion
This simple Todo API demonstrates how to create and manage an Express server with TypeScript. You can extend it by integrating a database like MongoDB or PostgreSQL and adding user authentication for a full-fledged application. Happy coding!