@bernierllc/contentful-webhook-service
v1.0.6
Published
Contentful webhook receiving, queueing, and event routing service with signature validation and retry logic
Readme
@bernierllc/contentful-webhook-service
Contentful webhook receiving, queueing, and event routing service with signature validation and retry logic.
Features
- ✅ HTTP webhook endpoint (
/webhooks/contentful) - ✅ HMAC-SHA256 signature validation
- ✅ Event queueing for reliable delivery
- ✅ Event routing to subscribers via event bus
- ✅ Automatic retry logic for failed processing
- ✅ Dead letter queue for permanently failed events
- ✅ Health check and stats endpoints
- ✅ NeverHub integration (optional)
- ✅ TypeScript with strict type safety
Installation
npm install @bernierllc/contentful-webhook-serviceQuick Start
import { ContentfulWebhookService } from '@bernierllc/contentful-webhook-service';
// Initialize service
const service = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!,
port: 3000,
retryAttempts: 3,
deadLetterQueueEnabled: true
});
// Subscribe to webhook events
service.on('contentful.Entry.publish', async (payload) => {
console.log('Entry published:', payload.sys.id);
// Process published entry
});
service.on('contentful.Entry.unpublish', async (payload) => {
console.log('Entry unpublished:', payload.sys.id);
// Handle unpublished entry
});
service.on('contentful.Asset.publish', async (payload) => {
console.log('Asset published:', payload.sys.id);
// Process published asset
});
// Start service
await service.start();
console.log('Webhook service running on port 3000');API Reference
ContentfulWebhookService
Main service class for handling Contentful webhooks.
Constructor
new ContentfulWebhookService(config: ContentfulWebhookServiceConfig)Config Options:
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| webhookSecret | string | ✅ | - | Contentful webhook secret for signature validation |
| port | number | ❌ | 3000 | Port to listen on |
| queueConfig | Partial<QueueConfig> | ❌ | {} | Custom queue configuration |
| logger | Logger | ❌ | Auto-created | Custom logger instance |
| enableNeverHub | boolean | ❌ | true | Enable NeverHub integration |
| retryAttempts | number | ❌ | 3 | Max retry attempts for failed processing |
| deadLetterQueueEnabled | boolean | ❌ | true | Enable dead letter queue |
Methods
start(port?: number): Promise<void>
Start the webhook service.
await service.start(3000);stop(): Promise<void>
Stop the webhook service gracefully.
await service.stop();on(event: string, handler: (data: any) => Promise<void>): void
Subscribe to webhook events.
service.on('contentful.Entry.publish', async (payload) => {
// Handle published entry
});Event Patterns:
contentful.Entry.publish- Entry publishedcontentful.Entry.unpublish- Entry unpublishedcontentful.Entry.save- Entry saved (draft)contentful.Entry.delete- Entry deletedcontentful.Asset.publish- Asset publishedcontentful.Asset.unpublish- Asset unpublishedcontentful.*- All webhook events (wildcard)
once(event: string, handler: (data: any) => Promise<void>): void
Subscribe to webhook events (one-time only).
service.once('contentful.Entry.publish', async (payload) => {
// Handle first published entry
});processQueue(): Promise<void>
Manually process queued webhooks (normally handled automatically).
await service.processQueue();getStats(): ServiceStats
Get current service statistics.
const stats = service.getStats();
console.log(`Received: ${stats.totalReceived}`);
console.log(`Processed: ${stats.totalProcessed}`);
console.log(`Failed: ${stats.totalFailed}`);HTTP Endpoints
POST /webhooks/contentful
Receive webhooks from Contentful.
Required Headers:
x-contentful-signature: HMAC-SHA256 signaturex-contentful-topic: Webhook topic (e.g.,Entry.publish)
Request Body:
{
"sys": {
"type": "Entry",
"id": "entry-123",
"space": { "sys": { "id": "space-123" } },
"contentType": { "sys": { "id": "blogPost" } }
},
"fields": {
"title": { "en-US": "My Blog Post" }
}
}Response:
{
"success": true,
"id": "webhook-1234567890-abc123"
}GET /health
Health check endpoint.
Response:
{
"status": "ok",
"stats": {
"totalReceived": 150,
"totalProcessed": 145,
"totalFailed": 2,
"queueSize": 3,
"deadLetterQueueSize": 2
},
"timestamp": "2025-01-01T00:00:00.000Z"
}GET /stats
Get current service statistics.
Response:
{
"totalReceived": 150,
"totalProcessed": 145,
"totalFailed": 2,
"queueSize": 3,
"deadLetterQueueSize": 2
}Usage Examples
Basic Usage
import { ContentfulWebhookService } from '@bernierllc/contentful-webhook-service';
const service = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!
});
service.on('contentful.Entry.publish', async (payload) => {
console.log('Entry published:', payload);
});
await service.start(3000);With Custom Queue Config
const service = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!,
queueConfig: {
maxSize: 5000,
maxRetries: 5
}
});With Custom Logger
import { createLogger } from '@bernierllc/logger';
const logger = createLogger({ level: 'debug' });
const service = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!,
logger
});Wildcard Event Handling
// Handle all webhook events
service.on('contentful.*', async (data) => {
console.log('Webhook received:', data.topic, data.payload.sys.id);
// Log to monitoring system, etc.
});Retry and Dead Letter Queue
const service = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!,
retryAttempts: 5,
deadLetterQueueEnabled: true
});
// Failed webhooks are automatically retried up to 5 times
// Permanently failed webhooks go to dead letter queueIntegration with Sync Service
import { ContentfulWebhookService } from '@bernierllc/contentful-webhook-service';
import { ContentfulSyncService } from '@bernierllc/contentful-sync-service';
const webhookService = new ContentfulWebhookService({
webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!
});
const syncService = new ContentfulSyncService({
spaceId: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!
});
// Sync on webhook events
webhookService.on('contentful.Entry.publish', async (payload) => {
await syncService.syncEntry(payload.sys.id);
});
await webhookService.start();Architecture
┌─────────────────┐
│ Contentful │
└────────┬────────┘
│ Webhook (HMAC signed)
▼
┌─────────────────────────┐
│ Webhook Endpoint │
│ - Signature validation │
│ - Payload parsing │
└────────┬────────────────┘
│
├─────────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌─────────────┐
│ Message Queue│ │ Event Bus │
│ - Reliable │ │ - Routing │
│ - Ordered │ │ - Pub/sub │
└──────┬───────┘ └──────┬──────┘
│ │
│ Retry │ Emit
│ │
▼ ▼
┌──────────────┐ ┌─────────────┐
│ Dead Letter │ │ Subscribers │
│ Queue │ │ (handlers) │
└──────────────┘ └─────────────┘Error Handling
The service handles errors gracefully:
- Invalid Signature: Returns 401 Unauthorized
- Missing Headers: Returns 401 Unauthorized
- Queue Full: Returns 500 Internal Server Error
- Handler Errors: Logged but don't block webhook acceptance
- Retry Failures: Moved to dead letter queue after max retries
MECE Compliance
Includes:
- ✅ HTTP webhook endpoint
- ✅ Signature validation
- ✅ Event queueing
- ✅ Event routing
- ✅ Retry logic
- ✅ Dead letter queue
Excludes:
- ❌ Webhook processing logic (delegate to subscribers)
- ❌ Content storage (use
@bernierllc/contentful-sync-service) - ❌ Cache invalidation (use
@bernierllc/contentful-cache-service)
Dependencies
@bernierllc/contentful-types- Contentful type definitions@bernierllc/contentful-webhook-handler- Signature validation@bernierllc/message-queue- Reliable message queueing@bernierllc/event-emitter- Event bus@bernierllc/logger- Structured logging@bernierllc/neverhub-adapter- NeverHub integrationexpress- HTTP server
Testing
npm test # Run tests in watch mode
npm run test:run # Run tests once
npm run test:coverage # Run tests with coverage reportTest Coverage
Target: 85%+ coverage (service package standard)
All critical paths are tested:
- Webhook receiving and validation
- Event queueing and routing
- Retry logic
- Dead letter queue
- Stats tracking
- Service lifecycle
License
Copyright (c) 2025 Bernier LLC. See LICENSE for details.
Related Packages
- @bernierllc/contentful-webhook-handler - Webhook validation
- @bernierllc/contentful-sync-service - Content synchronization
- @bernierllc/contentful-cache-service - Cache management
- @bernierllc/message-queue - Message queueing
- @bernierllc/event-emitter - Event bus
Support
For issues and feature requests, please create an issue on GitHub.
