Skip to main content

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 CodeMeaningUse Case
200OKSuccessful request
201CreatedResource created successfully
400Bad RequestInvalid input or missing required fields
401UnauthorizedAuthentication failed
500Internal Server ErrorServer-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

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]"}'

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

ErrorStatusMessageLocation
Missing credentials400Email y contraseña son obligatoriosLine 47-50
User not found401Usuario no encontrado con el email proporcionadoLine 52-55
Invalid password401Credenciales inválidasLine 57-60
Server error500Error al iniciar sesiónLine 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

1

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 });
  }
};
2

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 });
3

Use Appropriate Status Codes

  • 400 for client input errors
  • 401 for authentication failures
  • 404 for resources not found
  • 500 for server errors
4

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 });
}
5

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:
src/database/config.js
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

1

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' });
2

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' });
}
3

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.

Build docs developers (and LLMs) love