- Initialize Git repository with main branch - Create comprehensive .gitignore for Node.js, React, and environment files - Set up directory structure (frontend/, backend/, docs/) - Create detailed README.md with project overview and setup instructions - Add .env.example with all required environment variables - Configure Prettier for consistent code formatting All acceptance criteria met: ✅ Git repository initialized with appropriate .gitignore ✅ Directory structure matches Technical Assumptions ✅ README.md created with project overview and setup docs ✅ .env.example file with all required environment variables ✅ Prettier config files added for code formatting consistency 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
3.8 KiB
3.8 KiB
Error Handling Strategy
Error Flow
sequenceDiagram
participant User
participant FE as Frontend
participant API as Backend API
participant DB as Database
User->>FE: Submit invalid form
FE->>FE: Validate input
FE->>User: Show validation error (inline)
User->>FE: Submit valid form
FE->>API: POST request
API->>API: Validate input (express-validator)
alt Validation fails
API-->>FE: 400 Bad Request {error, details}
FE->>User: Display error message
else Validation passes
API->>DB: Query/mutation
alt Database error
DB-->>API: Error
API->>API: Log error
API-->>FE: 500 Server Error {error}
FE->>User: Display generic error
else Success
DB-->>API: Data
API-->>FE: 200/201 Success {data}
FE->>User: Display success message
end
end
Error Response Format
// Consistent error format across all API endpoints
interface ApiError {
error: string; // Human-readable error message
details?: Record<string, any>; // Optional validation details
timestamp?: string; // ISO timestamp
requestId?: string; // For debugging (optional)
}
// Examples:
// Validation error
{
"error": "Validation failed",
"details": {
"field": "deadlineDate",
"message": "Deadline must be in the future"
}
}
// Generic server error
{
"error": "Internal server error"
}
// Not found error
{
"error": "Book not found"
}
Frontend Error Handling
// services/api.js - Global error handler
async request(endpoint, options = {}) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
if (!response.ok) {
const errorData = await response.json();
throw new ApiError(errorData.error, errorData.details, response.status);
}
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
throw error; // Re-throw API errors
}
// Network errors or other fetch failures
throw new ApiError('Network error. Please check your connection.');
}
}
// Custom error class
class ApiError extends Error {
constructor(message, details, statusCode) {
super(message);
this.name = 'ApiError';
this.details = details;
this.statusCode = statusCode;
}
}
// Component error handling
try {
await booksService.addBook(bookData);
showSuccessMessage('Book added successfully!');
} catch (error) {
if (error instanceof ApiError) {
if (error.details) {
// Show field-specific errors
setFieldError(error.details.field, error.details.message);
} else {
// Show generic error
showErrorMessage(error.message);
}
} else {
showErrorMessage('An unexpected error occurred');
}
}
Backend Error Handling
// middleware/errorHandler.js - Centralized error handler
module.exports = (err, req, res, next) => {
// Log error for debugging
console.error('Error:', err);
// Prisma errors
if (err.code && err.code.startsWith('P')) {
if (err.code === 'P2025') {
return res.status(404).json({ error: 'Resource not found' });
}
if (err.code === 'P2002') {
return res.status(400).json({
error: 'Duplicate entry',
details: { field: err.meta?.target?.[0] }
});
}
return res.status(500).json({ error: 'Database error' });
}
// Validation errors (from express-validator)
if (err.errors && Array.isArray(err.errors)) {
return res.status(400).json({
error: 'Validation failed',
details: err.errors,
});
}
// Default: Internal server error
res.status(err.statusCode || 500).json({
error: err.message || 'Internal server error',
});
};
// Usage in server.js
app.use(errorHandler);