Hi, Devs!
Building a robust and maintainable REST API requires careful consideration of error handling, validation, and structure. In this article, we'll explore how to set up a custom error handling mechanism in a REST API using TypeScript, Express.js, Joi for validation, and principles of object-oriented programming (OOP). We'll structure our project as follows:
Scructure:
---src
-----middlewares
--------ErrorHanlder.ts
-----models
--------User.ts
-----repository
--------UserRepository.ts
-----routes
--------UserRouter.ts
-----controllers
--------UserController.ts
-----services
--------UserService.ts
-----validations
--------UserValidation.ts
---app.ts
---server.ts
Setting Up the Project
1 2 3 4 5 | mkdir rest-api
cd rest-api
npm init -y
npm install express typescript @types/node @types/express ts-node-dev
npx tsc --init
|
Copy after login
Middleware for Error Handling
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import { Request, Response, NextFunction } from 'express' ;
class ErrorHandler extends Error {
statusCode: number;
message: string;
constructor(statusCode: number, message: string) {
super();
this.statusCode = statusCode;
this.message = message;
}
}
const handleError = (err: ErrorHandler, req: Request, res: Response, next: NextFunction) => {
const { statusCode, message } = err;
res.status(statusCode).json({
status: 'error' ,
statusCode,
message,
});
};
export { ErrorHandler, handleError };
|
Copy after login
User Model
1 2 3 4 5 6 | export interface User {
id: number;
name: string;
email: string;
}
|
Copy after login
User Repository
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import { User } from '../models/User' ;
class UserRepository {
private users: User[] = [];
findAll(): User[] {
return this.users;
}
findById(id: number): User | undefined {
return this.users.find(user => user.id === id);
}
create(user: User): User {
this.users.push(user);
return user;
}
}
export default new UserRepository();
|
Copy after login
User Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import UserRepository from '../repository/UserRepository' ;
import { User } from '../models/User' ;
import { ErrorHandler } from '../middlewares/ErrorHandler' ;
class UserService {
getAllUsers(): User[] {
return UserRepository.findAll();
}
getUserById(id: number): User | undefined {
const user = UserRepository.findById(id);
if (!user) {
throw new ErrorHandler(404, 'User not found' );
}
return user;
}
createUser(user: User): User {
const { error } = userSchema.validate(user);
if (error) {
throw new ErrorHandler(400, error.details[0].message);
}
return UserRepository.create(user);
}
}
export default new UserService();
|
Copy after login
User Validation
1 2 3 4 5 6 7 8 9 10 | import Joi from 'joi' ;
const userSchema = Joi.object({
id: Joi.number().required(),
name: Joi.string().required(),
email: Joi.string().email().required(),
});
export { userSchema };
|
Copy after login
User Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import { Request, Response, NextFunction } from 'express' ;
import UserService from '../services/UserService' ;
import { ErrorHandler } from '../middlewares/ErrorHandler' ;
class UserController {
getAllUsers(req: Request, res: Response, next: NextFunction) {
try {
const users = UserService.getAllUsers();
res.json(users);
} catch (error) {
next(error);
}
}
getUserById(req: Request, res: Response, next: NextFunction) {
try {
const user = UserService.getUserById(parseInt(req.params.id));
res.json(user);
} catch (error) {
next(error);
}
}
createUser(req: Request, res: Response, next: NextFunction) {
try {
const user = UserService.createUser(req.body);
res.status(201).json(user);
} catch (error) {
next(error);
}
}
}
export default new UserController();
|
Copy after login
User Routes
1 2 3 4 5 6 7 8 9 10 11 | import { Router } from 'express' ;
import UserController from '../controllers/UserController' ;
const router = Router();
router.get( '/users' , UserController.getAllUsers);
router.get( '/users/:id' , UserController.getUserById);
router.post( '/users' , UserController.createUser);
export default router;
|
Copy after login
Application Entry Point
1 2 3 4 5 6 7 8 9 10 11 12 | import express from 'express' ;
import UserRouter from './routes/UserRouter' ;
import { handleError } from './middlewares/ErrorHandler' ;
const app = express();
app. use (express.json());
app. use ( '/api' , UserRouter);
app. use (handleError);
export default app;
|
Copy after login
Server Setup
1 2 3 4 5 6 7 8 | import app from './app' ;
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
|
Copy after login
By structuring our project using TypeScript, Express.js, Joi, and OOP principles, we achieve a clean and maintainable codebase. The custom error handling middleware provides a consistent way to manage errors across the application, while the validation logic ensures data integrity. This setup can serve as a solid foundation for more complex applications.
Contacts
Email: luizcalaca@gmail.com
Instagram: https://www.instagram.com/luizcalaca
Linkedin: https://www.linkedin.com/in/luizcalaca/
Twitter: https://twitter.com/luizcalaca
The above is the detailed content of Custom error handling in a REST API using TypeScript, Express.js, Joi validation, and object-oriented programming. For more information, please follow other related articles on the PHP Chinese website!