Error Recovery Patterns
Automatic Retry with Circuit Breaker
Implement circuit breaker pattern to prevent cascading failures:
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
}
}
}
const breaker = new CircuitBreaker();
async function makeRequest(url, options) {
return breaker.execute(() => fetch(url, options));
}
Graceful Degradation
Provide fallback behavior when API is unavailable:
async function getUserData(userId) {
try {
return await apiRequest(`/api/service/users/${userId}`);
} catch (error) {
if (error.status >= 500) {
// Server error - use cached data if available
const cached = localStorage.getItem(`user_${userId}`);
if (cached) {
console.warn('Using cached user data due to server error');
return JSON.parse(cached);
}
}
throw error;
}
}