Careeroza
Typing Requests and Responses
Request Generics
Express types Request<Params, ResBody, ReqBody, Query> and Response<ResBody> are generic. Filling them in types req.params, req.body, req.query and what res.json() accepts.
import express, { type Request, type Response } from "express";
type Product = { id: number; name: string; price: number; stock: number };
type CreateProductBody = Omit<Product, "id">;
type ListQuery = { page?: string; q?: string };
type ErrorBody = { message: string };
const products: Product[] = [];
const app = express();
app.use(express.json());
app.get("/products", (req: Request<{}, Product[], {}, ListQuery>, res: Response<Product[]>) => {
const page = Number(req.query.page ?? "1");
const q = req.query.q?.toLowerCase() ?? "";
res.json(products.filter((p) => p.name.toLowerCase().includes(q)).slice((page - 1) * 20, page * 20));
});
app.get("/products/:id", (req: Request<{ id: string }>, res: Response<Product | ErrorBody>) => {
const product = products.find((p) => p.id === Number(req.params.id));
if (!product) return res.status(404).json({ message: "Product not found" });
res.json(product);
});
app.post("/products", (req: Request<{}, Product, CreateProductBody>, res: Response<Product>) => {
const product = { id: products.length + 1, ...req.body }; // validate req.body first in real code!
products.push(product);
res.status(201).json(product);
});
app.listen(3000, () => console.log("API on http://localhost:3000"));Example
curl -X POST localhost:3000/products -H "Content-Type: application/json" -d '{"name":"Kettle","price":1299,"stock":10}'Example output
HTTP/1.1 201 Created
{"id":1,"name":"Kettle","price":1299,"stock":10}Typing req.body Does Not Validate It
Note: Typing req.body as CreateProductBody does not validate anything; a client can send any JSON. Parse it with a schema (Zod, or the mini schema from the Type-safe Patterns module) and return 400 on failure.