Express | TypeScript | MongoDB | Docker. Part 2
In this second part of the introduction to Express, TypeScript, MongoDB, and Docker, we will expand our RESTful API by adding validation, global error handling, and structured logging.
Areas for Improvement
Starting from a basic CRUD application, several components are required to make it production-ready:
- Data Validation: Validate incoming request bodies and parameters using libraries like
express-validator. - Error Handling: Implement global error-handling middleware to capture exceptions and format consistent HTTP responses.
- Authentication Middleware: Secure endpoints using JWT verification middleware.
- Logging Middleware: Record incoming HTTP requests with timestamp, method, and route details.
- API Documentation: Integrate OpenAPI/Swagger for interactive endpoint documentation.
Data Validation with express-validator
Install express-validator:
npm install express-validator
Create a reusable validation handler middleware:
src/middlewares/validation.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { validationResult } from 'express-validator';
export const handleValidationErrors = (req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
Route-Level Validation Example
src/routes/boardgame.routes.ts
import { Router } from 'express';
import { body } from 'express-validator';
import { handleValidationErrors } from '../middlewares/validation.middleware';
import { createBoardGame } from '../controllers/boardgame.controller';
const router = Router();
router.post(
'/',
[
body('title').notEmpty().withMessage('Title is required'),
body('minPlayers').isInt({ min: 1 }).withMessage('Minimum players must be at least 1'),
body('maxPlayers').isInt({ min: 1 }).withMessage('Maximum players must be at least 1'),
handleValidationErrors
],
createBoardGame
);
export default router;
Global Error Handling Middleware
Create a centralized error handler:
src/middlewares/error.middleware.ts
import { Request, Response, NextFunction } from 'express';
export class CustomError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
}
}
export const errorHandler = (
err: Error | CustomError,
req: Request,
res: Response,
next: NextFunction
) => {
const statusCode = err instanceof CustomError ? err.statusCode : 500;
const message = err.message || 'Internal Server Error';
res.status(statusCode).json({
status: 'error',
statusCode,
message
});
};
Register the error handler in src/index.ts after all routes:
app.use(errorHandler);