@fjell/registry
Version:
Dependency injection and service location system for the Fjell ecosystem
357 lines (296 loc) • 9.67 kB
Markdown
# Error Handling
Comprehensive error handling and resilience features for production-ready applications.
## Overview
The Fjell Client API includes a robust error handling system designed for production environments. It provides automatic retry logic, custom error types, enhanced context, and configurable recovery strategies.
## Key Features
### 🔄 **Automatic Retry Logic**
- Exponential backoff with jitter
- Smart error classification (retryable vs non-retryable)
- Configurable retry strategies
- Rate limiting respect
### 🎯 **Custom Error Types**
- Specific error classes for different scenarios
- Enhanced error context and debugging information
- Structured error data for programmatic handling
- Business-friendly error messages
### 🛡️ **Production Resilience**
- Circuit breaker patterns
- Graceful degradation strategies
- Custom error handlers for monitoring integration
- Business workflow error recovery
## Error Types
| Error Type | Code | Retryable | Description |
|------------|------|-----------|-------------|
| **NetworkError** | `NETWORK_ERROR` | ✅ | Connection failures, DNS issues |
| **TimeoutError** | `TIMEOUT_ERROR` | ✅ | Request timeouts |
| **ServerError** | `SERVER_ERROR` | ✅ | 5xx HTTP status codes |
| **RateLimitError** | `RATE_LIMIT_ERROR` | ✅ | 429 Too Many Requests |
| **AuthenticationError** | `AUTHENTICATION_ERROR` | ❌ | 401 Unauthorized |
| **AuthorizationError** | `AUTHORIZATION_ERROR` | ❌ | 403 Forbidden |
| **NotFoundError** | `NOT_FOUND_ERROR` | ❌ | 404 Not Found |
| **ValidationError** | `VALIDATION_ERROR` | ❌ | 400 Bad Request |
| **ConflictError** | `CONFLICT_ERROR` | ❌ | 409 Conflict |
| **PayloadTooLargeError** | `PAYLOAD_TOO_LARGE_ERROR` | ❌ | 413 Request Too Large |
## Error Scenarios
### [Network Errors and Timeouts](./network-errors.md)
Handle connection failures, DNS issues, and request timeouts:
```typescript
try {
const user = await userApi.get(userKey);
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
// Automatically retried with exponential backoff
console.log('Network recovered after retries');
}
}
```
### [Authentication and Authorization](./auth-errors.md)
Manage authentication failures and permission issues:
```typescript
try {
const data = await api.get(key);
} catch (error) {
if (error.code === 'AUTHENTICATION_ERROR') {
// Redirect to login
redirectToLogin();
}
}
```
### [Validation Errors](./validation-errors.md)
Handle request validation and data format issues:
```typescript
try {
const user = await userApi.create(userData);
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
// Display field-specific errors
error.validationErrors.forEach(err => {
showFieldError(err.field, err.message);
});
}
}
```
### [Server Errors and Retry Logic](./server-errors.md)
Automatic retry for server-side failures:
```typescript
// Server errors (5xx) are automatically retried
const user = await userApi.create(userData);
// Will retry up to configured maximum on 500, 502, 503 errors
```
### [Rate Limiting](./rate-limiting.md)
Respect API rate limits with appropriate delays:
```typescript
try {
const results = await api.all(query);
} catch (error) {
if (error.code === 'RATE_LIMIT_ERROR') {
// Automatically retried after rate limit period
console.log('Rate limit recovered');
}
}
```
### [Custom Error Handling](./custom-errors.md)
Implement custom error handlers for monitoring and business logic:
```typescript
const config = {
errorHandler: (error, context) => {
// Send to monitoring service
monitoring.recordError(error, context);
// Business-specific error handling
if (error.code === 'PAYMENT_FAILED') {
sendPaymentFailureNotification(context.customerId);
}
}
};
```
## Configuration
### Basic Configuration
```typescript
const config = {
baseUrl: 'https://api.example.com',
retryConfig: {
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 30000,
backoffMultiplier: 2,
enableJitter: true
},
enableErrorHandling: true
};
const userApi = createPItemApi<User, 'user'>('user', ['users'], config);
```
### Advanced Configuration
```typescript
const enterpriseConfig = {
baseUrl: 'https://api.enterprise.com',
retryConfig: {
maxRetries: 5,
initialDelayMs: 500,
maxDelayMs: 60000,
backoffMultiplier: 1.5,
enableJitter: true,
// Custom retry logic
shouldRetry: (error, attemptNumber) => {
if (error.code === 'PAYMENT_ERROR') {
return attemptNumber < 2; // Limited retries for payments
}
return error.isRetryable && attemptNumber < 5;
},
// Custom retry callback
onRetry: (error, attemptNumber, delay) => {
logger.warn(`Retrying API call (attempt ${attemptNumber + 1})`, {
errorCode: error.code,
delay
});
}
},
// Custom error handler
errorHandler: (error, context) => {
// Centralized error logging
logger.error('API Operation Failed', {
error: error.message,
code: error.code,
operation: context.operation,
duration: context.duration
});
// Send to error tracking
errorTracking.captureException(error, { extra: context });
// Business logic
if (error.code === 'RATE_LIMIT_ERROR') {
circuitBreaker.recordFailure();
}
}
};
```
## Best Practices
### 1. **Handle Specific Error Types**
```typescript
try {
const result = await api.operation();
} catch (error) {
switch (error.code) {
case 'VALIDATION_ERROR':
handleValidationErrors(error.validationErrors);
break;
case 'AUTHENTICATION_ERROR':
redirectToLogin();
break;
case 'NETWORK_ERROR':
showNetworkErrorMessage();
break;
default:
showGenericErrorMessage(error.message);
}
}
```
### 2. **Implement Graceful Degradation**
```typescript
async function getUserWithFallback(userKey: PriKey<'user'>): Promise<User | null> {
try {
return await userApi.get(userKey);
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
// Use cached data
return await getCachedUser(userKey);
}
if (error.code === 'NOT_FOUND_ERROR') {
// Return null gracefully
return null;
}
// Re-throw unexpected errors
throw error;
}
}
```
### 3. **Monitor Error Patterns**
```typescript
const errorMetrics = {
recordError: (error: ClientApiError, context: any) => {
metrics.increment('api.errors.total', {
error_code: error.code,
operation: context.operation
});
if (error.isRetryable) {
metrics.increment('api.errors.retryable');
}
}
};
```
### 4. **Business Workflow Recovery**
```typescript
async function createOrderWithRecovery(orderData: OrderData) {
const transaction = await beginTransaction();
try {
const order = await orderApi.create(orderData);
const payment = await paymentApi.create({ orderId: order.id });
await transaction.commit();
return order;
} catch (error) {
await transaction.rollback();
// Compensating actions
if (error.code === 'PAYMENT_FAILED') {
await notificationService.sendPaymentFailure(orderData.customerId);
}
throw error;
}
}
```
## Testing Error Scenarios
### Unit Testing
```typescript
describe('Error Handling', () => {
it('should retry on network errors', async () => {
const mockApi = createMockApi();
mockApi.httpGet.mockRejectedValueOnce(new NetworkError('Connection failed'));
mockApi.httpGet.mockResolvedValueOnce({ id: 'user-123' });
const user = await userApi.get(userKey);
expect(user.id).toBe('user-123');
expect(mockApi.httpGet).toHaveBeenCalledTimes(2);
});
it('should not retry on validation errors', async () => {
const mockApi = createMockApi();
mockApi.httpPost.mockRejectedValue(new ValidationError('Invalid data'));
await expect(userApi.create({})).rejects.toThrow('Invalid data');
expect(mockApi.httpPost).toHaveBeenCalledTimes(1);
});
});
```
### Integration Testing
```typescript
describe('Error Recovery', () => {
it('should handle server downtime gracefully', async () => {
// Simulate server being down then recovering
server.use(
rest.get('/api/users/:id', (req, res, ctx) => {
return res.once(ctx.status(500));
}),
rest.get('/api/users/:id', (req, res, ctx) => {
return res(ctx.json({ id: 'user-123' }));
})
);
const user = await userApi.get(userKey);
expect(user.id).toBe('user-123');
});
});
```
## Production Checklist
- [ ] Configure appropriate retry strategies for your use case
- [ ] Implement custom error handlers for monitoring and alerting
- [ ] Set up error tracking (Sentry, Datadog, etc.)
- [ ] Add business-specific error recovery logic
- [ ] Implement graceful degradation for critical paths
- [ ] Monitor error rates and patterns
- [ ] Test error scenarios in staging environments
- [ ] Document error handling procedures for your team
- [ ] Set up alerts for critical error patterns
- [ ] Implement circuit breakers for external services
## Related Documentation
- [Configuration Guide](../configuration.md) - Configure error handling behavior
- [Operations](../operations/README.md) - Error handling in specific operations
- [Examples](../../examples-README.md) - Error handling examples and patterns
## Next Steps
1. Review specific error scenario documentation
2. Configure error handling for your environment
3. Implement custom error handlers for your monitoring stack
4. Test error scenarios in your application
5. Set up monitoring and alerting for production