- Updated skill documentation files to include structured metadata for better organization. - Enhanced bilingual descriptions and guidelines for clarity in both English and Vietnamese. - Refined sections on usage, best practices, and related skills to ensure consistency across all documentation. - Improved formatting and removed outdated references to streamline the documentation experience. - Added best practices checklists to relevant skills for better usability and adherence to standards.
9.4 KiB
name, description
| name | description |
|---|---|
| error-handling-patterns | Error handling patterns and conventions for GoodGo microservices. Use when implementing error handling, creating custom error classes, handling exceptions, standardizing error responses, or debugging error scenarios. |
Error Handling Patterns
When to Use This Skill
Use this skill when:
- Implementing error handling in services, controllers, or repositories
- Creating custom error classes for specific error scenarios
- Standardizing error responses across APIs
- Handling exceptions from external services or database operations
- Implementing error middleware and global error handlers
- Debugging error scenarios and improving error messages
- Distinguishing between operational and programming errors
Core Concepts
Error Types
-
Operational Errors: Expected errors that occur during normal operation
- Examples: Validation errors, authentication failures, resource not found
- Should be handled gracefully and return appropriate HTTP status codes
- Safe to expose error details to clients (with caution)
-
Programming Errors: Unexpected errors due to bugs in code
- Examples: Null pointer exceptions, type errors, logic bugs
- Should be logged with full details for debugging
- Should return generic error messages to clients (hide implementation details)
Error Code System
The platform uses a centralized error code system (ErrorCode enum) that:
- Provides unique identifiers for each error type
- Maps to HTTP status codes consistently
- Enables error tracking and analytics
- Supports internationalization
Error codes follow the pattern: {CATEGORY}_{NUMBER}
AUTH_001- Authentication errorsVALIDATION_001- Validation errorsRESOURCE_001- Resource errorsDB_001- Database errors
Patterns
Base Error Class: HttpError
All custom errors extend the HttpError base class:
export class HttpError extends Error {
public readonly statusCode: number;
public readonly errorCode: string;
public readonly isOperational: boolean;
public readonly details?: any;
constructor(
message: string,
statusCode: number = 500,
errorCode: string = 'INTERNAL_ERROR',
isOperational: boolean = true,
details?: any
) {
super(message);
this.statusCode = statusCode;
this.errorCode = errorCode;
this.isOperational = isOperational;
this.details = details;
Error.captureStackTrace(this, this.constructor);
}
toApiResponse() {
return {
success: false,
error: {
code: this.errorCode,
message: this.message,
...(this.details && { details: this.details }),
},
timestamp: new Date().toISOString(),
};
}
}
Standard Error Classes
Use these predefined error classes for common scenarios:
Resource Errors:
NotFoundError- 404: Resource not foundConflictError- 409: Resource conflict (e.g., duplicate)
Validation Errors:
ValidationError- 422: Input validation failedBadRequestError- 400: Invalid request
Authentication/Authorization:
UnauthorizedError- 401: Authentication requiredForbiddenError- 403: Access denied
System Errors:
InternalServerError- 500: Internal server error (programming error)ServiceUnavailableError- 503: Service temporarily unavailableDatabaseError- 500: Database operation failedExternalServiceError- 502: External service error
Rate Limiting:
RateLimitError- 429: Too many requests
Error Code Enum
Centralized error codes in ErrorCode enum:
export enum ErrorCode {
// Authentication & Authorization
UNAUTHORIZED = 'AUTH_001',
FORBIDDEN = 'AUTH_002',
INVALID_TOKEN = 'AUTH_003',
TOKEN_EXPIRED = 'AUTH_004',
// Validation
VALIDATION_ERROR = 'VALIDATION_001',
INVALID_FORMAT = 'VALIDATION_002',
// Resources
NOT_FOUND = 'RESOURCE_001',
ALREADY_EXISTS = 'RESOURCE_002',
CONFLICT = 'RESOURCE_003',
// Database
DATABASE_ERROR = 'DB_001',
CONSTRAINT_VIOLATION = 'DB_004',
// System
INTERNAL_ERROR = 'SYS_001',
RATE_LIMIT_EXCEEDED = 'SYS_003',
}
Using Errors in Services
import { NotFoundError, ConflictError } from '../errors/http-error';
import { ErrorCode } from '../errors/error-codes';
export class UserService {
async getUserById(id: string) {
const user = await this.repository.findById(id);
if (!user) {
throw new NotFoundError('User', { id });
}
return user;
}
async createUser(data: CreateUserInput) {
const existing = await this.repository.findByEmail(data.email);
if (existing) {
throw new ConflictError('User with this email already exists');
}
return await this.repository.create(data);
}
}
Error Middleware Pattern
Global error handler middleware processes all errors:
export const errorHandler = (
err: any,
req: express.Request,
res: express.Response,
_next: express.NextFunction
): void => {
let statusCode = 500;
let errorCode = ErrorCode.INTERNAL_ERROR;
let message = 'Internal server error';
let isOperational = false;
// Handle HttpError instances
if (err instanceof HttpError) {
statusCode = err.statusCode;
errorCode = err.errorCode as ErrorCode;
message = err.message;
isOperational = err.isOperational;
}
// Handle Prisma errors
else if (err.code === 'P2002') {
statusCode = 409;
errorCode = ErrorCode.CONSTRAINT_VIOLATION;
message = 'Resource already exists';
isOperational = true;
}
// Handle Zod validation errors
else if (err.name === 'ZodError') {
statusCode = 422;
errorCode = ErrorCode.VALIDATION_ERROR;
message = 'Validation failed';
// Extract validation details
}
// Log error
if (!isOperational || statusCode >= 500) {
logger.error('Unhandled error', { error: err, statusCode, errorCode });
} else {
logger.warn('Operational error', { error: err, statusCode, errorCode });
}
// Send response
const response = {
success: false,
error: {
code: errorCode,
message: isProduction && statusCode >= 500
? 'Internal server error'
: message,
},
timestamp: new Date().toISOString(),
};
res.status(statusCode).json(response);
};
Async Error Wrapper
Wrap async route handlers to catch promise rejections:
export const asyncHandler = (fn: Function) => {
return (req: express.Request, res: express.Response, next: express.NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
// Usage
router.get('/users/:id', asyncHandler(async (req, res) => {
const user = await userService.getUserById(req.params.id);
res.json({ success: true, data: user });
}));
Error Response Format
Standardized error response format:
{
success: false,
error: {
code: "RESOURCE_001",
message: "User not found",
details?: {
// Optional additional details (not in production for 5xx errors)
}
},
timestamp: "2024-01-01T00:00:00.000Z"
}
Best Practices
- Use Specific Error Classes: Use the most specific error class available
- Include Context: Provide helpful error messages with context
- Mark Operational Errors: Set
isOperational: truefor expected errors - Don't Expose Internal Details: Hide implementation details in production
- Log Appropriately: Use
logger.error()for programming errors,logger.warn()for operational errors - Handle Database Errors: Map Prisma errors to appropriate HTTP errors
- Use Error Codes: Always use
ErrorCodeenum for consistency - Validate Early: Validate input early to catch errors before processing
Common Mistakes
- Not Using Error Classes: Using generic
Errorinstead of specific error classes - Exposing Stack Traces: Including stack traces in production responses
- Ignoring Errors: Not handling errors in async operations
- Generic Error Messages: Using vague error messages without context
- Not Logging: Forgetting to log errors for debugging
- Wrong HTTP Status Codes: Using incorrect status codes for error types
- Not Using Error Middleware: Handling errors manually instead of using middleware
Troubleshooting
Error Not Caught by Middleware
Problem: Error not being caught by error middleware
Solution: Ensure error middleware is added last, after all routes. Use asyncHandler for async route handlers.
Generic Error Messages in Production
Problem: Generic "Internal server error" shown even for operational errors
Solution: Check isOperational flag is set correctly. Verify error middleware handles all error types.
Error Code Not Found
Problem: Error code not in ErrorCode enum
Solution: Add error code to enum following naming convention. Update ERROR_CODE_TO_STATUS mapping.
Stack Traces Exposed
Problem: Stack traces visible in API responses Solution: Ensure production environment checks are in place. Use error middleware to filter stack traces.
Resources
- Error Classes - Base error classes
- Error Codes - Error code definitions
- Error Middleware - Global error handler
- API Design - API response formats
- Security - Security error handling