Error Handling
Problem
Failed API requests leave users staring at loading spinners indefinitely or crash components with unhandled promise rejections. There’s no consistent way to show error messages, retry failed requests, or gracefully degrade functionality when backend services are unavailable.
Solution
Anticipate and handle failure cases explicitly rather than letting errors propagate uncaught. This creates graceful degradation where applications display helpful messages instead of breaking completely.
Example
This example demonstrates wrapping API calls in try-catch blocks to handle errors gracefully and prevent unhandled promise rejections.
async function fetchUser(id) {
try {
// Attempt to fetch user data
const response = await fetch(`/api/users/${id}`);
// Check if the HTTP response indicates success
if (!response.ok) throw new Error('User not found');
return await response.json();
} catch (error) {
// Log the error for debugging and monitoring
console.error('Failed to fetch user:', error);
// Return null instead of throwing to allow graceful degradation
return null;
}
}
Benefits
- Prevents uncaught errors from crashing the application.
- Enables showing helpful error messages to users.
- Provides opportunity to retry failed operations.
- Can log errors for monitoring and debugging.
- Creates better user experience during failures.
Tradeoffs
- Adds boilerplate try-catch blocks throughout the codebase.
- Can hide errors if not properly logged or reported.
- May encourage swallowing errors silently.
- Requires deciding how to handle each error type.
- Can make code more verbose and harder to read.