
Express + TypeScript - CRUD Boilerplate
Introduction
In this post, I demonstrate how to build a simple CRUD application as an extension of the boilerplate created in Express + TypeScript - Project Configuration. This article will focus on routing and extracting data from HTTP requests. Database topics are omitted to keep this article concise and will be covered in later parts of this series.
The complete code discussed in this article is available in the GitHub repository.
- Express + TypeScript - Project Configuration
- Express + TypeScript - ESLint and Prettier
- Express + TypeScript - CRUD Boilerplate
- Express + TypeScript - Application Structure
- Express + TypeScript - MongoDB Configuration
- Express + TypeScript - Request Validation with Joi
- Express + TypeScript - Application Middleware
First Server
The src/index.ts file, created in the previously linked article, is very simple and doesn't require detailed discussion. It imports necessary elements from the express framework, initializes the application, registers a basic HTTP request handler, and finally starts listening on port 5000.
import express, { Request, Response } from "express";
const app = express();
app.get("/", (req: Request, res: Response) => {
res.send("Hello World!");
});
app.listen(5000, () => {
console.log("Server started on port 5000!");
});
Routing
CRUD Application
CRUD stands for Create - Read - Update - Delete, describing a standard way of building simple services operating on a given data entity. The application must allow for creating, retrieving, updating, and deleting records.
To extend the application, I start by defining a simple data storage object to simulate a database (actual database integration will be covered in future articles). This will be a simple list containing objects with a predefined type Book, representing a basic book entity. The service simulates a simplified library application, which I will gradually expand.
import express from "express";
const app = express();
const randomId = (): string => Math.random().toString(36).slice(2, 7);
interface Book {
id: string;
title: string;
author: string;
published: number;
}
const books: Book[] = [
{
id: randomId(),
title: "Ice",
author: "Dukaj",
published: 2007,
},
];
// Routing
// GET all books
// GET single book
// POST new book
// PUT (update) book
// DELETE book
app.listen(5000, () => {
console.log("Server started on port 5000!");
});
CRUD Endpoints
GET All Books
app.get("/api/books", (req: Request, res: Response) => {
res.json({ data: books });
});
POST a New Book
app.post("/api/books", (req: Request, res: Response) => {
const { title, author, published } = req.body;
const newBook = { id: randomId(), title, author, published };
books.push(newBook);
res.status(201).json({ data: newBook });
});
GET a Single Book by ID
app.get("/api/books/:id", (req: Request, res: Response) => {
const { id } = req.params;
const book = books.find((item) => item.id === id);
res.json({ data: book });
});
DELETE a Book by ID
app.delete("/api/books/:id", (req: Request, res: Response) => {
const { id } = req.params;
books = books.filter((book) => book.id !== id);
res.status(204).send();
});
PUT (Update) a Book by ID
app.put("/api/books/:id", (req: Request, res: Response) => {
const { id } = req.params;
const { title, author, published } = req.body;
const existingBook = books.find((item) => item.id === id);
if (!existingBook) {
const newBook = { id: randomId(), title, author, published };
books.push(newBook);
return res.status(201).json({ data: newBook });
}
const updatedBook: Book = {
id,
title: title || existingBook.title,
author: author || existingBook.author,
published: published || existingBook.published,
};
books = books.map((book) => (book.id === id ? updatedBook : book));
res.json({ data: updatedBook });
});
Summary
Even though this was a lengthy explanation, the application remains a single-file project. However, what we now have is a simple CRUD application that provides a solid foundation for further development. The complete code for this application is available in the repository.
Upcoming articles will cover topics such as database integration, proper error handling, middleware usage, and more.