Loading lessons...
Project: Full-Stack Express/TypeScript API Boilerplate
Project 3: Express / TypeScript REST API
Architect a production-ready Express API with generic Request handlers, middleware typing, and custom error handling.
Custom Request Interface & Error Handling
import { Request, Response, NextFunction } from "express";
export interface AuthenticatedRequest<Params = any, ResBody = any, ReqBody = any>
extends Request<Params, ResBody, ReqBody> {
user?: {
id: string;
role: "admin" | "user";
};
}
export class AppError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
Async Handler Wrapper & Controller
type AsyncController<Req extends Request = Request> = (
req: Req,
res: Response,
next: NextFunction
) => Promise<void | Response>;
export const asyncHandler = (fn: AsyncController) => {
return (req: Request, res: Response, next: NextFunction) => {
fn(req, res, next).catch(next);
};
};
// Controller logic
interface CreateUserDTO {
username: string;
email: string;
}
export const createUser = asyncHandler(
async (req: AuthenticatedRequest<{}, {}, CreateUserDTO>, res: Response) => {
const { username, email } = req.body;
if (!username || !email) {
throw new AppError(400, "Username and email required");
}
return res.status(201).json({ status: "success", data: { username, email } });
}
);
TL;DR
- Extends standard Express
Requestinterfaces with custom payload/user properties. - Wraps async controllers to automatically forward promise rejections to Express error middleware.
- Enforces strict DTO types on request bodies.