Handling Failures
Learn how to build resilient webhook integrations that gracefully handle failures and ensure reliable event processing.
Common Failure Scenarios
Network Issues
- Connection timeouts
- DNS resolution failures
- Intermittent connectivity
Handler Issues
- Application downtime
- Database connection failures
- Processing logic errors
- Memory/resource exhaustion
Data Issues
- Malformed JSON payloads
- Unexpected event structures
- Missing required fields
- Invalid data types
Retry Strategies
Exponential Backoff
const delay = (attempt) => Math.pow(2, attempt) * 1000; // 1s, 2s, 4s, 8s...
async function retryWebhook(webhook, maxAttempts = 3) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
await processWebhook(webhook);
return; // Success
} catch (error) {
if (attempt === maxAttempts - 1) throw error;
await new Promise(resolve => setTimeout(resolve, delay(attempt)));
}
}
}
Circuit Breaker Pattern
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.threshold = threshold;
this.timeout = timeout;
this.failureCount = 0;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async call(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;
}
}
}
Error Recovery
Dead Letter Queues
const failedEvents = [];
app.post('/webhooks', async (req, res) => {
try {
await processWebhook(req.body);
res.status(200).json({ success: true });
} catch (error) {
// Add to dead letter queue for manual processing
failedEvents.push({
event: req.body,
error: error.message,
timestamp: new Date(),
retryCount: 0
});
res.status(500).json({ error: 'Processing failed' });
}
});
Graceful Degradation
app.post('/webhooks', async (req, res) => {
try {
// Critical processing
await updateOrderStatus(req.body);
try {
// Non-critical processing
await sendEmailNotification(req.body);
} catch (error) {
// Log but don't fail the webhook
console.warn('Email notification failed:', error);
}
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ error: 'Critical processing failed' });
}
});
Monitoring and Alerting
Health Checks
app.get('/health', (req, res) => {
const health = {
status: 'healthy',
database: checkDatabaseConnection(),
memory: process.memoryUsage(),
uptime: process.uptime()
};
res.json(health);
});
Error Tracking
const Sentry = require('@sentry/node');
app.post('/webhooks', async (req, res) => {
try {
await processWebhook(req.body);
res.status(200).json({ success: true });
} catch (error) {
Sentry.captureException(error, {
tags: {
component: 'webhook-handler'
},
extra: {
webhookData: req.body
}
});
res.status(500).json({ error: 'Processing failed' });
}
});
This section is under development. More failure handling patterns coming soon.