Introduction
Amazon EventBridge is AWS's core service for routing events at scale, connecting S3, Lambda, DynamoDB, and third-party services through event buses. In 2026, amid the serverless boom, it shines in event-driven architectures by filtering millions of events per second without polling. This advanced tutorial walks you through building a custom bus, rules with sophisticated JSON matching, a schema registry for validation, and Lambda targets with dead-letter queues. Picture a pipeline where an S3 upload kicks off ML analysis via EventBridge—that's the power we'll implement using AWS CDK for reproducible infrastructure as code. By the end, you'll have a resilient, monitored system ready for production.
Prerequisites
- AWS account with IAM admin rights (or CDK deploy permissions)
- Node.js 20+ and AWS CDK 2.140+ installed
- AWS CLI v2 configured (
aws configure) - Advanced knowledge of TypeScript, Lambda, and JSON events
- AWS region
eu-west-1for this example
Bootstrap CDK and Initialize Project
npm install -g aws-cdk@2
mkdir eventbridge-advanced && cd eventbridge-advanced
cdk init app --language typescript
npm install aws-cdk-lib constructs @aws-sdk/client-eventbridge
cdk bootstrap aws://$(aws sts get-caller-identity --query Account --output text)/eu-west-1This script installs CDK globally, sets up an empty TypeScript project, adds CDK libs and the EventBridge SDK, then bootstraps the environment for assets like Lambda zips. Stick to regions with EventBridge support; verify with cdk ls.
CDK Stack Structure
We'll create an EventBridgeStack with a custom bus, a rule that filters user.signup events matching a specific pattern, a schema registry for validation, and a Lambda target with a DLQ. CDK synthesizes this into CloudFormation for declarative management.
EventBus Stack and Basic Rule
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class EventBridgeStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const eventBus = new events.EventBus(this, 'CustomEventBus', {
eventBusName: 'MyAdvancedEventBus'
});
const handler = new lambda.Function(this, 'EventHandler', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = (event) => { console.log(event); return { statusCode: 200 }; };')
});
const rule = eventBus.addRule(this, 'SignupRule', {
ruleName: 'user-signup-rule',
eventPattern: {
source: ['myapp'],
detailType: ['user.signup'],
detail: {
userType: ['premium']
}
}
});
rule.addTarget(new targets.LambdaFunction(handler));
}
}This stack creates a custom EventBus named 'MyAdvancedEventBus', a simple inline Lambda as the target, and a rule that matches myapp:user.signup events where detail.userType=premium. The JSON pattern enables precise filtering; CDK auto-grants permissions.
Adding Schemas and DLQ
Analogy: An EventBridge schema acts like a strict JSON contract, validating incoming payloads much like Pydantic in Python. We'll register it in the Schema Registry and link it to the rule to reject malformed events.
Schema Registry and Advanced DLQ
{
"type": "object",
"title": "UserSignupSchema",
"required": ["userId", "email", "userType"],
"properties": {
"userId": { "type": "string", "pattern": "^[a-zA-Z0-9]{10,}$" },
"email": { "type": "string", "format": "email" },
"userType": { "type": "string", "enum": ["basic", "premium"] },
"metadata": { "type": "object" }
}
}This JSON schema validates signup events: userId alphanumeric >10 chars, valid email, userType enum. Import it via CLI or CDK and associate with the rule; non-compliant events route to an auto-created SQS DLQ.
Integrating Schema and DLQ in Stack
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as events from 'aws-cdk-lib/aws-events';
import * as schemas from 'aws-cdk-lib/aws-eventschemas';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as iam from 'aws-cdk-lib/aws-iam';
export class EventBridgeStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const eventBus = new events.EventBus(this, 'CustomEventBus', { eventBusName: 'MyAdvancedEventBus' });
const schema = new schemas.CfnRegistrySchema(this, 'SignupSchema', {
registryName: 'MySchemaRegistry',
schemaName: 'UserSignup',
type: 'OpenApi3',
content: JSON.stringify({
openapi: '3.0.0',
components: { schemas: { UserSignup: { /* schéma JSON ci-dessus converti */ type: 'object', properties: { userId: {type:'string'}, email:{type:'string'}, userType:{type:'string'} } } } }
})
});
const dlq = new sqs.Queue(this, 'DLQ');
const handler = new lambda.Function(this, 'EventHandler', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = async (event) => { console.log(JSON.stringify(event, null, 2)); return { statusCode: 200 }; };'),
deadLetterQueueEnabled: true,
deadLetterQueue: dlq
});
eventBus.grantPutEventsTo(handler);
const rule = eventBus.addRule(this, 'SignupRule', {
ruleName: 'user-signup-rule',
eventPattern: {
source: ['myapp'],
detailType: ['user.signup'],
detail: {
userType: ['premium']
}
},
deadLetterQueue: dlq
});
rule.addTarget(new targets.LambdaFunction(handler));
}
}Enhancements: Adds OpenAPI3 Schema Registry (based on the JSON schema), SQS DLQ for retries, and auto IAM grants. Schemas validate at runtime; invalid events route to DLQ. CDK handles cross-service dependencies.
Deployment and App Configuration
In bin/eventbridge-advanced.ts, instantiate the stack: new EventBridgeStack(app, 'EventBridgeStack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'eu-west-1' } });. Run npm run build && cdk deploy.
Testing with AWS CLI PutEvents
EVENT_DATA='{"source":"myapp","detail-type":"user.signup","detail":{"userId":"user123abcde","email":"user@premium.com","userType":"premium"}}'
aws events put-events --entries '{"EventBusName":"MyAdvancedEventBus","Source":"myapp","DetailType":"user.signup","Detail":"'$EVENT_DATA'"}' --region eu-west-1
# Événement invalide pour DLQ
aws events put-events --entries '{"EventBusName":"MyAdvancedEventBus","Source":"myapp","DetailType":"user.signup","Detail":"{\"userType\":\"invalid\"}"}' --region eu-west-1
# Vérif DLQ
aws sqs receive-message --queue-url $(aws sqs get-queue-attributes --queue-url $(aws events describe-event-bus --name MyAdvancedEventBus --query 'DeadLetterQueueArn' --output text | cut -d: -f7-) --attribute-names ApproximateNumberOfMessages --output text | grep -o 'https[^ ]*') --region eu-west-1This script sends a valid event (matches rule, invokes Lambda) and an invalid one (hits DLQ). Use receive-message to check DLQ; adjust URL as needed. Debug Lambda logs in CloudWatch.
Advanced Lambda Handler with Parsing
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
export const handler = async (event: any): Promise<any> => {
console.log('Event reçu:', JSON.stringify(event, null, 2));
const detail = JSON.parse(event.detail || '{}');
if (detail.userType !== 'premium') {
throw new Error('User non premium');
}
// Simule traitement async (ex: appel DynamoDB)
await new Promise(resolve => setTimeout(resolve, 100));
return {
statusCode: 200,
body: JSON.stringify({ message: `Traitement OK pour ${detail.userId}`, processedAt: new Date().toISOString() })
};
};This Node.js 20 handler parses event.detail, validates userType, and simulates async processing. Errors trigger DLQ. For production, package with esbuild or CDK Assets; use structured logs for X-Ray.
Monitoring with CloudWatch Alarms
import * as cdk from 'aws-cdk-lib';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as events from 'aws-cdk-lib/aws-events';
// Dans le stack :
const eventBus = /* ... */;
new cloudwatch.Alarm(this, 'HighFailedInvocations', {
metric: eventBus.metricFailedInvocations(),
threshold: 5,
evaluationPeriods: 2,
alarmDescription: 'Plus de 5 invocations échouées par période'
});
new cloudwatch.Alarm(this, 'DLQDepthAlarm', {
metric: dlq.metricApproximateNumberOfMessagesVisible(),
threshold: 10,
evaluationPeriods: 1
});Adds CloudWatch alarms for EventBridge failed invocations and DLQ depth. Thresholds: 5 over 2 periods for the bus, 10 for DLQ. Integrate SNS for alerts; use metric IDs for custom dashboards.
Best Practices
- Advanced Filtering: Use
detail-mapfor nested JSON; test patterns in the EventBridge Console. - Security: Apply least-privilege IAM with CDK
grantPutEventsTo; enable KMS encryption on buses. - Scalability: Batch multiple targets (Lambda + Step Functions); leverage Partner Events for SaaS.
- Schema-First: Always use the registry for safe evolution; version schemas.
- Replay: Archive events to S3 for bug replays.
Common Errors to Avoid
- Forgetting
eventBus.grantPutEventsTo(target): Producers get 403 errors. - Overly broad patterns:
{"source":["*"]}overloads Lambda; refine withdetail.userId[0] == "A". - No DLQ: Events lost on Lambda throttling; always set
deadLetterQueueEnabled: true. - Unlinked schemas: Validation skipped; bind via
schemaIdin the rule.
Next Steps
- AWS Docs: EventBridge Developer Guide
- CDK Patterns: AWS Samples GitHub
- Advanced: Integrate Pipes for ETL transforms.