Good Code
The good version gives routes small helpers for success and error responses, making the API contract consistent.
Lesson 07
Return predictable success and error envelopes across routes.
import type { Response } from "express";
// Small helpers keep response envelopes consistent.
export function ok<T>(res: Response, data: T) {
return res.json({ data });
}
export function created<T>(res: Response, data: T) {
return res.status(201).json({ data });
}
export function fail(res: Response, status: number, message: string) {
return res.status(status).json({ error: { message } });
}router.get("/", async (_req, res) => {
// Neighboring routes now need different client parsing rules.
res.json(await service.listOpenReviews());
});
router.post("/", async (req, res) => {
res.status(201).json({ review: await service.createReview(req.body) });
});
router.get("/:id", async (req, res) => {
const review = await service.getReview(req.params.id);
if (!review) {
res.status(404).send("not found");
return;
}
res.json({ data: { review } });
});The good version gives routes small helpers for success and error responses, making the API contract consistent.
The bad version returns arrays, objects, strings, and nested data shapes depending on the route.