Overview
The Construction Backend API implements structured error handling patterns across all endpoints to ensure consistent, informative error responses. This guide documents the error handling strategies used throughout the application.
Error Response Structure
All API errors follow a consistent JSON format:
{
"message": "Human-readable error description",
"error": "Detailed error information (when available)"
}
HTTP Status Codes
The API uses standard HTTP status codes to indicate request outcomes:
| Status Code | Meaning | Use Case |
|---|
200 | OK | Successful request |
201 | Created | Resource created successfully |
400 | Bad Request | Invalid input or missing required fields |
401 | Unauthorized | Authentication failed |
500 | Internal Server Error | Server-side error occurred |
Error Handling Patterns
User Registration Errors
The user registration endpoint implements multiple validation checks. From src/controllers/userController.js:4-40:
src/controllers/userController.js
const registerUser = async (req, res) => {
try {
const { document, email, password, name, last_name, cellphone, user_type } = req.body;
// Validation: Required fields
if (!email || !password) {
return res.status(400).json({
message: 'Email y contraseña son obligatorios'
});
}
// Validation: Duplicate email
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({
message: 'El correo electrónico ya está registrado'
});
}
// Validation: Duplicate document
const existingDocument = await User.findOne({ document });
if (existingDocument) {
return res.status(400).json({
message: 'La cédula ya está registrada'
});
}
// Hash password and create user
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = new User({
document, email, password: hashedPassword,
name, last_name, cellphone, user_type
});
await newUser.save();
res.status(201).json({
message: 'Usuario registrado correctamente',
user: newUser
});
} catch (error) {
console.error('Error al registrar el usuario:', error);
res.status(500).json({
message: 'Error al registrar el usuario',
error: error.message
});
}
};
Registration Error Types
Missing Required Fields
Duplicate Email
Duplicate Document
Server Error
Status Code: 400 Bad RequestTrigger: Email or password not providedResponse:{
"message": "Email y contraseña son obligatorios"
}
Example Request:curl -X POST http://localhost:3000/api/user/register \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
Status Code: 400 Bad RequestTrigger: Email already exists in databaseResponse:{
"message": "El correo electrónico ya está registrado"
}
This check happens at src/controllers/userController.js:12-15
Status Code: 400 Bad RequestTrigger: Document ID already exists in databaseResponse:{
"message": "La cédula ya está registrada"
}
This check happens at src/controllers/userController.js:17-20
Status Code: 500 Internal Server ErrorTrigger: Database connection issues, unexpected errorsResponse:{
"message": "Error al registrar el usuario",
"error": "Specific error message"
}
Server errors are logged to console at src/controllers/userController.js:37
User Login Errors
The login endpoint implements authentication validation. From src/controllers/userController.js:43-74:
src/controllers/userController.js
const loginUser = async (req, res) => {
try {
const { email, password } = req.body;
// Validation: Required fields
if (!email || !password) {
return res.status(400).json({
message: 'Email y contraseña son obligatorios'
});
}
// Validation: User exists
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({
message: 'Usuario no encontrado con el email proporcionado'
});
}
// Validation: Password correct
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({
message: 'Credenciales inválidas'
});
}
// Success response
const userInfo = {
document: user.document,
email: user.email,
name: user.name,
last_name: user.last_name,
cellphone: user.cellphone,
user_type: user.user_type,
};
res.status(200).json({
message: 'Usuario logueado correctamente',
user: userInfo
});
} catch (error) {
res.status(500).json({
message: 'Error al iniciar sesión',
error
});
}
};
Login Error Types
| Error | Status | Message | Location |
|---|
| Missing credentials | 400 | Email y contraseña son obligatorios | Line 47-50 |
| User not found | 401 | Usuario no encontrado con el email proporcionado | Line 52-55 |
| Invalid password | 401 | Credenciales inválidas | Line 57-60 |
| Server error | 500 | Error al iniciar sesión | Line 71-73 |
Notice the use of 401 Unauthorized for authentication failures versus 400 Bad Request for validation failures.
Quotation Errors
The quotation endpoints handle data retrieval and creation errors. From src/controllers/quotationController.js:
Get Quotations
src/controllers/quotationController.js
const getQuotations = async (req, res) => {
try {
const quotations = await Quotation.find();
res.json(quotations);
} catch (error) {
res.status(500).json({
message: 'Error al obtener cotizaciones',
error
});
}
};
Register Quotation
src/controllers/quotationController.js
const registerQuotation = async (req, res) => {
try {
const data = req.body;
// Comprehensive validation
if (!data || !data.factory || !data.fix ||
!data.description_quotation ||
data.subtotal === undefined ||
data.unexpected === undefined ||
data.iva === undefined ||
data.administratitive === undefined ||
data.utility === undefined ||
data.total_price === undefined ||
!Array.isArray(data.sections)) {
return res.status(400).json({
error: "Formato de JSON inválido"
});
}
const newQuotation = new Quotation(data);
await newQuotation.save();
res.status(201).json({
message: 'Cotización almacenada correctamente',
quotation: newQuotation
});
} catch (error) {
res.status(500).json({
message: 'Error al almacenar la cotización',
error
});
}
};
The quotation validation at src/controllers/quotationController.js:18-24 checks for multiple required fields. Ensure all fields are provided to avoid validation errors.
Quotation Error Types
Get Quotations:
- Status:
500
- Message: Error al obtener cotizaciones
- Cause: Database query failure
Register Quotation:
- Status:
400 - Invalid JSON format (missing required fields)
- Status:
500 - Database save failure
Database Connection Errors
Database connection errors are handled at the connection level in src/database/connection.js:4-23:
src/database/connection.js
export const connectDB = async () => {
try {
await mongoose.connect(MONGO_URI);
mongoose.connection.on('connected', () => {
console.log('Connected to MongoDB Atlas');
});
mongoose.connection.on('error', (err) => {
console.error('MongoDB connection error:', err);
});
mongoose.connection.on('disconnected', () => {
console.log('Disconnected from MongoDB Atlas');
});
} catch (error) {
console.error('Error connecting to MongoDB Atlas:', error);
}
};
Connection Error Handling
- Initial connection error: Logged to console but doesn’t crash the app
- Runtime errors: Handled by the
error event listener
- Disconnection: Logged via
disconnected event listener
Connection errors are logged but don’t throw exceptions. This allows the application to start even if MongoDB is temporarily unavailable.
Error Handling Best Practices
Always Use Try-Catch
Wrap async operations in try-catch blocks to prevent unhandled promise rejections:const myEndpoint = async (req, res) => {
try {
// Your code here
} catch (error) {
res.status(500).json({ message: 'Error message', error });
}
};
Validate Early
Check required fields and validate input before performing expensive operations:// Good: Validate before database queries
if (!email || !password) {
return res.status(400).json({ message: 'Required fields missing' });
}
// Then perform database operations
const user = await User.findOne({ email });
Use Appropriate Status Codes
400 for client input errors
401 for authentication failures
404 for resources not found
500 for server errors
Log Errors for Debugging
Always log errors to console or monitoring service:catch (error) {
console.error('Error context:', error);
res.status(500).json({ message: 'User-friendly message', error: error.message });
}
Return Consistent Error Format
Maintain consistent error response structure across all endpoints:{
"message": "Human-readable error",
"error": "Technical details (optional)"
}
Common Error Scenarios
Missing Environment Variables
Problem: MONGO_URI is undefined
Solution:
import dotenv from 'dotenv';
dotenv.config();
export const MONGO_URI = process.env.MONGO_URI;
if (!MONGO_URI) {
console.error('MONGO_URI is not defined in environment variables');
process.exit(1);
}
Mongoose Validation Errors
Problem: Schema validation fails
Solution: Handle Mongoose validation errors specifically:
catch (error) {
if (error.name === 'ValidationError') {
return res.status(400).json({
message: 'Validation failed',
errors: error.errors
});
}
res.status(500).json({ message: 'Server error', error });
}
Duplicate Key Errors
Problem: Unique constraint violation
Solution:
catch (error) {
if (error.code === 11000) {
return res.status(400).json({
message: 'Duplicate value detected',
field: Object.keys(error.keyPattern)[0]
});
}
res.status(500).json({ message: 'Server error', error });
}
Testing Error Scenarios
Test Missing Required Fields
curl -X POST http://localhost:3000/api/user/register \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
Expected Response:
{
"message": "Email y contraseña son obligatorios"
}
Test Duplicate Email
# First request (should succeed)
curl -X POST http://localhost:3000/api/user/register \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "password123",
"document": "12345",
"name": "Test",
"last_name": "User",
"cellphone": "1234567890",
"user_type": "client"
}'
# Second request with same email (should fail)
curl -X POST http://localhost:3000/api/user/register \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "password456",
"document": "67890",
"name": "Another",
"last_name": "User",
"cellphone": "0987654321",
"user_type": "admin"
}'
Expected Response:
{
"message": "El correo electrónico ya está registrado"
}
Test Invalid Login
curl -X POST http://localhost:3000/api/user/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "wrongpassword"
}'
Expected Response:
{
"message": "Usuario no encontrado con el email proporcionado"
}
Monitoring and Debugging
Enable Detailed Error Logging
For development, add detailed error logging:
catch (error) {
console.error('Error details:', {
message: error.message,
stack: error.stack,
name: error.name,
code: error.code
});
res.status(500).json({ message: 'Error message', error: error.message });
}
Production Error Handling
For production, avoid exposing error details:
catch (error) {
console.error('Production error:', error);
// Send generic message to client
res.status(500).json({
message: 'An error occurred processing your request'
});
// Log detailed error to monitoring service
// logToMonitoring(error);
}
Never expose sensitive error details (stack traces, database errors) to clients in production.
Security Considerations
Sanitize Error Messages
Don’t expose internal system details in error messages:// Bad: Exposes database structure
res.status(500).json({ error: error.stack });
// Good: Generic message
res.status(500).json({ message: 'Processing error occurred' });
Rate Limit Error Responses
Implement rate limiting to prevent error-based attacks:// Prevent brute force on login errors
if (loginAttempts > 5) {
return res.status(429).json({ message: 'Too many attempts' });
}
Validate All Input
Always validate and sanitize user input before processing:if (!email || !email.includes('@')) {
return res.status(400).json({ message: 'Invalid email format' });
}
Summary
The Construction Backend API implements comprehensive error handling:
- Consistent error response format across all endpoints
- Appropriate HTTP status codes (400, 401, 500)
- Early validation to catch errors before expensive operations
- Detailed logging for debugging while protecting sensitive information
- User-friendly messages that guide clients to correct usage
By following these patterns, you can build robust, maintainable API endpoints that handle errors gracefully and provide excellent developer experience.