- 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>
162 lines
3.8 KiB
Markdown
162 lines
3.8 KiB
Markdown
# Error Handling Strategy
|
|
|
|
## Error Flow
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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);
|
|
```
|
|
|
|
---
|