Error Handling
Relevant source files
The following files were used as context for generating this wiki page:
The error handling system in the CDIO4 project is implemented on both the backend and frontend to manage and respond to runtime issues, validation failures, and other exceptional conditions. The backend features a dedicated, centralized error handling middleware designed to catch and format various error types into a standardized JSON response. However, the current implementation in server.js bypasses this middleware in favor of local try...catch blocks within API route handlers.
On the frontend, components like the sign-in page use try...catch blocks to handle API response errors. This involves managing an error state, which is then used to conditionally render user-friendly error messages, ensuring a clear feedback loop for the user.
Backend Error Handling
The backend has a two-fold approach to error handling: a designed centralized middleware and an implemented local try...catch strategy in route handlers.
Centralized Error Middleware (errorHandler.js)
A sophisticated error handling middleware is defined in backend/src/middleware/errorHandler.js. Its purpose is to intercept all errors occurring in the Express application, log them, and format them into a consistent response structure for the client.
Key Features:
- Error Logging: It logs detailed error information to the console, including the message, stack (in development), URL, method, and IP address.
- Error Type Specialization: It identifies specific error types from Mongoose (e.g.,
CastError,ValidationError) and JWT (e.g.,JsonWebTokenError,TokenExpiredError) and maps them to appropriate HTTP status codes and user-friendly messages. - Standardized Response: It ensures all errors are sent to the client in a uniform JSON format.
Sources: backend/src/middleware/errorHandler.js:7-60
Middleware Logic Flow
The following diagram illustrates the logical flow within the errorHandler middleware.
This diagram shows how the errorHandler middleware processes an incoming error, classifies it, and prepares a standardized response.
Sources: backend/src/middleware/errorHandler.js:7-60
Specific Error Handling
The middleware is designed to handle several common error types explicitly.
| Error Name / Code | Source | Handled As | Status Code |
|---|---|---|---|
CastError | Mongoose | Invalid ObjectId format | 404 |
11000 | Mongoose | Duplicate key (e.g., unique email) | 400 |
ValidationError | Mongoose | Schema validation failed | 400 |
JsonWebTokenError | JWT | Malformed or invalid token | 401 |
TokenExpiredError | JWT | Expired token | 401 |
| Default | Any | Unhandled exception | 500 |
Sources: backend/src/middleware/errorHandler.js:22-48
Standard Error Response Structure
All errors processed by the middleware are formatted into the following JSON structure before being sent to the client.
{
"success": false,
"error": {
"message": "Specific error message here",
"stack": "...stack trace in development..."
},
"timestamp": "2023-10-27T10:00:00.000Z"
}
Sources: backend/src/middleware/errorHandler.js:51-59
Implemented Route-Level Handling
Despite the existence of the errorHandler middleware, it is not registered in the main application file, backend/src/server.js. Instead, API route handlers implement their own error handling using try...catch blocks. This local handling catches exceptions and sends a generic 500 error response.
// File: backend/src/server.js:83-92
app.post("/users", async (req, res) => {
try {
const { name, email, password } = req.body; // Lấy dữ liệu từ body request
const newUser = new user({ name, email, password }); // Tạo mới user
await newUser.save(); // Lưu user vào database
res.status(201).json(newUser); // Trả về user vừa tạo
} catch (error) {
console.error("Lỗi khi tạo người dùng mới:", error);
res.status(500).json({ error: "Lỗi server" });
}
});
This implementation means that the specific error formatting (like for validation or duplicate keys) from errorHandler.js is not currently being utilized in the user creation endpoint.
Sources: backend/src/server.js:63-92
Frontend Error Handling
The frontend application handles errors received from the backend API, primarily within component logic responsible for making API calls.
Login Error Flow
The SignIn component provides a clear example of client-side error management. When a user attempts to log in, the handleSubmit function wraps the API call in a try...catch block.
- The
loginfunction fromAuthContextis called. - If the API response indicates failure (
res.successis false), an error message from the response is stored in the component'serrorstate. - If the API call itself fails (e.g., network error), the
catchblock sets a generic error message. - The
errorstate is then used to conditionally render an error message to the user.
The sequence diagram below details this process.
This diagram shows the sequence of events during a failed login attempt, from user action to the display of an error message.
Sources: frontend/src/pages/Sign_in.js:26-50
Displaying Error Messages
The SignIn component renders the error message conditionally using the error state variable.
// File: frontend/src/pages/Sign_in.js:90-92
<form onSubmit={handleSubmit} className="space-y-5">
{error && (
<p className="text-red-400 text-sm text-center">{error}</p>
)}
{/* ... form inputs ... */}
</form>
Styling for general error alerts is also defined globally, indicating a design pattern for displaying errors.
/* File: frontend/src/styles/index.css:200-204 */
.alert-error {
background-color: #f8d7da;
color: #721c24;
border-left-color: #dc3545;
}
Sources: frontend/src/pages/Sign_in.js:91-93, frontend/src/styles/index.css:200-204
Data Validation Errors
Errors can also originate from data validation at different layers of the backend.
Model-Level Validation
The Mongoose User schema defines several validation rules. If a new or updated user object violates these rules upon saving, Mongoose will throw a ValidationError.
name: Is required and has a max length of 50.email: Is required, must be unique, and must match a regex pattern.password: Is required and has a min length of 6.
// File: backend/src/models/User.js:14-33
name: {
type: String,
required: [true, "Vui lòng cung cấp tên người dùng"],
trim: true,
maxlength: [50, "Tên không thể dài hơn 50 ký tự"],
},
email: {
type: String,
required: [true, "Vui lòng cung cấp email"],
unique: true,
// ...
},
password: {
type: String,
required: [true, "Vui lòng cung cấp mật khẩu"],
minlength: [6, "Mật khẩu phải có ít nhất 6 ký tự"],
// ...
},
This ValidationError is one of the specific types the errorHandler.js middleware is designed to handle, converting it into a 400 Bad Request response.
Sources: backend/src/models/User.js:14-33, backend/src/middleware/errorHandler.js:35-38
Route-Level Validation
The application also uses express-validator for input validation at the route level, as seen in users.js. This provides an earlier layer of validation before the data reaches the controller logic. Failures at this stage would also generate errors that need to be caught and handled.
Sources: backend/src/routes/users.js:23-34
Summary
The project has a well-designed but currently unused centralized error handling middleware on the backend. The active implementation relies on local try...catch blocks in API routes, which provide basic error handling but lack the specificity of the middleware. The frontend effectively consumes backend errors, using component state to provide clear feedback to the user. This dual-layered approach is fundamental to maintaining application robustness and a positive user experience.