AWS Lambda Event Sources Explained

By Oleksandr Andrushchenko — Published on — Modified on

AWS Lambda Event Sources Explained
AWS Lambda Event Sources Explained

AWS Lambda is event-driven by design. A Lambda function usually does not run by itself. It runs because something invokes it: an HTTP request, uploaded file, queue message, database stream record, schedule, or event from another AWS service.

These triggers are called event sources. Understanding Lambda event sources is one of the most important skills for building serverless applications, because the event source defines how your function receives data, how retries work, whether batching is available, how scaling behaves, and what failure handling strategy you need.

Table of Contents

Introduction to AWS Lambda Event Sources

An event source is anything that can invoke a Lambda function. It can be an AWS service, an HTTP endpoint, a queue, a stream, a database change, a schedule, or direct invocation through the AWS SDK.

The important part is not only what triggers Lambda, but also how the trigger behaves. API Gateway waits for a response. S3 invokes Lambda asynchronously. SQS is polled by Lambda through an event source mapping. Kinesis sends records in batches. Step Functions uses Lambda as one step in a workflow.

What Is an Event Source?

An event source produces the input event that Lambda receives. For example, when API Gateway invokes Lambda, the event may contain the HTTP method, path, headers, query parameters, and request body.

{
  "httpMethod": "GET",
  "path": "/users/42",
  "queryStringParameters": {
    "includeOrders": "true"
  }
}

For S3, the event looks completely different because the input is about a bucket and object key.

{
  "Records": [
    {
      "eventSource": "aws:s3",
      "s3": {
        "bucket": {
          "name": "uploads-bucket"
        },
        "object": {
          "key": "images/photo.jpg"
        }
      }
    }
  ]
}

Key point: the Lambda handler signature may look the same, but the event structure depends on the event source.

How Lambda Receives Events

Lambda can receive events in several ways:

  • Direct synchronous invocation: the caller waits for the Lambda response.
  • Asynchronous invocation: Lambda queues the event and processes it later.
  • Event source mapping: Lambda polls a stream or queue and invokes the function with records.
  • Service integration: another AWS service invokes Lambda as part of its workflow.
Invocation Style How It Works Examples
Synchronous Caller waits for function response API Gateway, Function URL, ALB
Asynchronous Event is queued and Lambda returns immediately to the source S3, SNS, EventBridge
Poll-based Lambda polls records from the source and invokes the function in batches SQS, Kinesis, DynamoDB Streams, Kafka
Workflow-based A workflow service calls Lambda as one step Step Functions, CodePipeline

Synchronous vs Asynchronous Invocation

Synchronous invocation means the caller waits for the result. This is common for HTTP APIs. If Lambda fails, the caller usually receives an error response.

Asynchronous invocation means the event source sends the event to Lambda and does not wait for the function result. Lambda processes the event in the background and handles retries according to async invocation settings.

Behavior Synchronous Asynchronous
Caller waits for response Yes No
Good for APIs Yes No
Good for background work No Yes
Failure handling Returned to caller Retries, destinations, or dead-letter queue

HTTP-Based Event Sources

HTTP-based event sources are used when Lambda should respond to web requests. They are common for APIs, webhooks, backend-for-frontend layers, internal tools, and lightweight services.

Amazon API Gateway

Amazon API Gateway is the most common way to expose Lambda as an HTTP API. It can handle routing, authentication, throttling, request validation, custom domains, stages, and integrations with other AWS services.

When to Use

  • REST APIs for web and mobile applications.
  • Public APIs with authentication, throttling, and custom domains.
  • Webhook receivers from third-party systems.
  • Microservice endpoints where each route maps to one Lambda function or a small group of functions.

Example Handler

import json

def lambda_handler(event, context):
    user_id = event["pathParameters"]["userId"]

    user = {
        "id": user_id,
        "name": "Demo User"
    }

    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps(user)
    }

Advantages

  • Good API features: routing, throttling, authorization, stages, custom domains.
  • Serverless: no web server management.
  • Scalable: works well for many request/response workloads.

Limitations

  • Timeouts matter: not suitable for long-running request processing.
  • Cold starts can affect latency: especially for heavier runtimes or VPC functions.
  • API design can become messy: if routing and function boundaries are not planned.

Rule of thumb: use API Gateway when Lambda is the backend for a real HTTP API.

AWS Lambda Function URLs

Lambda Function URLs provide a simple HTTPS endpoint directly for a Lambda function. They are easier than API Gateway but have fewer API management features.

When to Use

  • Simple webhooks
  • Internal tools
  • Quick prototypes
  • Single-function endpoints

Example Handler

import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "Hello from Lambda Function URL"
        })
    }

Function URL vs API Gateway

Feature Function URL API Gateway
Setup complexity Very simple More configuration
Routing Basic, function-level Advanced route management
API management Limited Stronger API features
Best for Simple endpoint Production API

Rule of thumb: use Function URLs for simple endpoints, not for complex public APIs.

Application Load Balancer

Application Load Balancer can also invoke Lambda. This is useful when Lambda is part of an architecture that already uses ALB routing.

When to Use

  • Existing ALB-based architectures
  • Path-based routing between containers, EC2, and Lambda
  • Internal HTTP services
  • Hybrid compute where some routes go to ECS and some go to Lambda

Example Handler

import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "statusDescription": "200 OK",
        "isBase64Encoded": False,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps({
            "message": "Response from Lambda behind ALB"
        })
    }

Rule of thumb: use ALB + Lambda when Lambda is one target type inside a broader load-balanced architecture.

Storage Event Sources

Storage event sources trigger Lambda when files or objects change. This is useful for import pipelines, image processing, document handling, data lake ingestion, and file validation.

Amazon S3 Events

Amazon S3 can invoke Lambda when an object is created, deleted, restored, or changed depending on the configured event type.

When to Use

  • Image resizing after upload.
  • CSV or JSON import into a database.
  • PDF processing or metadata extraction.
  • Antivirus scanning for uploaded files.
  • Data lake ingestion after files land in S3.

Example S3 Event

{
  "Records": [
    {
      "eventSource": "aws:s3",
      "eventName": "ObjectCreated:Put",
      "s3": {
        "bucket": {
          "name": "my-upload-bucket"
        },
        "object": {
          "key": "uploads/report.csv"
        }
      }
    }
  ]
}

Example Handler

def lambda_handler(event, context):
    for record in event["Records"]:
        bucket = record["s3"]["bucket"]["name"]
        key = record["s3"]["object"]["key"]

        print(f"Processing s3://{bucket}/{key}")

    return {
        "processed": len(event["Records"])
    }

Important Warning

Avoid recursive triggers. If Lambda writes output to the same S3 prefix that triggered it, the output file can invoke the same function again.

Bad:
uploads/file.csv -> Lambda -> writes uploads/processed-file.csv -> Lambda runs again

Better:
uploads/file.csv -> Lambda -> writes processed/file.csv

Rule of thumb: use separate input and output prefixes or separate buckets.

Messaging Event Sources

Messaging event sources are used when work should be processed asynchronously. They are often better than doing slow work inside an HTTP request.

Amazon SQS

Amazon SQS is one of the most important Lambda event sources. Lambda polls the queue, receives messages in batches, invokes your function, and deletes messages after successful processing.

When to Use

  • Background jobs
  • Email sending
  • Report generation
  • Order processing
  • Retryable external API calls
  • Buffering traffic spikes

Example SQS Event

{
  "Records": [
    {
      "messageId": "msg-1",
      "body": "{\"orderId\":\"ord_123\",\"amount\":99.95}"
    }
  ]
}

Example Handler

import json

def lambda_handler(event, context):
    for record in event["Records"]:
        message = json.loads(record["body"])
        print("Processing order:", message["orderId"])

    return {
        "processed": len(event["Records"])
    }

Best Practices

  • Configure a dead-letter queue for messages that fail repeatedly.
  • Use batch size carefully. Larger batches improve throughput but complicate failure handling.
  • Use partial batch responses when only some records fail.
  • Set visibility timeout longer than the Lambda timeout.
  • Make processing idempotent because duplicate delivery can happen.

Rule of thumb: use SQS when work can be done later and should survive temporary failures.

Amazon SNS

Amazon SNS is a publish/subscribe service. A publisher sends a message to a topic, and SNS delivers it to subscribers such as Lambda functions, SQS queues, HTTP endpoints, or email addresses.

When to Use

  • Fan-out notifications
  • One event to multiple consumers
  • Simple pub/sub
  • Notification pipelines

Example SNS Event

{
  "Records": [
    {
      "EventSource": "aws:sns",
      "Sns": {
        "Subject": "OrderCreated",
        "Message": "{\"orderId\":\"ord_123\"}"
      }
    }
  ]
}

Example Handler

import json

def lambda_handler(event, context):
    for record in event["Records"]:
        message = json.loads(record["Sns"]["Message"])
        print("Received SNS message:", message)

    return {
        "processed": len(event["Records"])
    }

SNS vs SQS

Service Best For Pattern
SNS Broadcasting one event to many subscribers Fan-out
SQS Reliable background processing Queue worker
SNS + SQS Durable fan-out with independent consumers Reliable event distribution

Rule of thumb: use SNS when multiple systems should react to the same event.

Amazon MQ

Amazon MQ is a managed message broker service for Apache ActiveMQ and RabbitMQ. Lambda can consume messages from supported Amazon MQ brokers.

When to Use

  • Migration from RabbitMQ or ActiveMQ
  • Enterprise messaging systems
  • Legacy applications that already use broker-based messaging
  • AMQP or JMS-style workflows

Example Handler Idea

def lambda_handler(event, context):
    for record in event["rmqMessagesByQueue"]:
        print("Processing broker message:", record)

    return {
        "status": "done"
    }

Rule of thumb: use Amazon MQ when you need compatibility with existing broker-based systems, not when SQS is enough.

Event-Driven Event Sources

Event-driven sources are used when systems communicate through facts that already happened, such as OrderCreated, UserRegistered, or PaymentFailed.

Amazon EventBridge

Amazon EventBridge is an event bus service. It can receive events from AWS services, custom applications, SaaS integrations, and schedules, then route those events to targets such as Lambda.

When to Use

  • Domain events
  • Decoupled microservices
  • SaaS integrations
  • Event routing by source, type, or payload fields
  • Scheduled jobs

Example EventBridge Event

{
  "source": "app.orders",
  "detail-type": "OrderCreated",
  "detail": {
    "orderId": "ord_123",
    "customerId": "cus_456",
    "amount": 120.50
  }
}

Example Handler

def lambda_handler(event, context):
    detail_type = event["detail-type"]
    detail = event["detail"]

    print(f"Received event type: {detail_type}")
    print("Event detail:", detail)

    return {
        "status": "processed"
    }

EventBridge vs SNS

Feature SNS EventBridge
Primary model Topic-based pub/sub Event bus with routing rules
Filtering Subscription filtering Advanced event pattern matching
Best for Simple fan-out Event-driven architecture
Event structure Message-oriented Business-event-oriented

Rule of thumb: use EventBridge when events are part of your application architecture, not just notifications.

AWS CloudTrail Events

AWS CloudTrail records AWS API activity. CloudTrail events can be routed through EventBridge and used to trigger Lambda functions for security, compliance, automation, or governance workflows.

When to Use

  • Security monitoring
  • Detecting sensitive AWS API calls
  • Automatic remediation
  • Compliance workflows

Example Use Case

CloudTrail detects security group change
EventBridge matches the event
Lambda validates the change
Lambda sends alert or reverts unsafe rule

Rule of thumb: use CloudTrail events when Lambda should react to AWS account activity.

Amazon EventBridge Scheduler

EventBridge Scheduler can invoke Lambda on a schedule. This is useful for cron-like jobs without maintaining a server or cron daemon.

When to Use

  • Nightly cleanup jobs
  • Hourly synchronization
  • Scheduled reports
  • Periodic health checks
  • Delayed one-time tasks

Schedule Examples

rate(5 minutes)
rate(1 hour)
cron(0 8 * * ? *)

Example Handler

def lambda_handler(event, context):
    print("Running scheduled job")
    run_cleanup()

    return {
        "status": "complete"
    }

def run_cleanup():
    print("Cleanup logic here")

Rule of thumb: use Scheduler when time itself is the event source.

Database Event Sources

Database event sources trigger Lambda when data changes. They are useful for audit logs, search indexing, denormalization, notifications, and event-driven projections.

Amazon DynamoDB Streams

DynamoDB Streams capture changes to DynamoDB table items. Lambda can process insert, update, and delete events from the stream.

When to Use

  • Audit logging
  • Search index synchronization
  • Denormalized views
  • Notifications after data changes
  • Event-driven projections

Example Stream Event

{
  "Records": [
    {
      "eventName": "INSERT",
      "eventSource": "aws:dynamodb",
      "dynamodb": {
        "NewImage": {
          "id": {
            "S": "user_123"
          },
          "email": {
            "S": "alex@example.com"
          }
        }
      }
    }
  ]
}

Example Handler

def lambda_handler(event, context):
    for record in event["Records"]:
        event_name = record["eventName"]

        if event_name == "INSERT":
            new_image = record["dynamodb"]["NewImage"]
            print("New item:", new_image)

        elif event_name == "MODIFY":
            print("Item updated")

        elif event_name == "REMOVE":
            print("Item deleted")

    return {
        "processed": len(event["Records"])
    }

Important Warning

Avoid infinite update loops. If Lambda reacts to a DynamoDB change and writes back to the same table, that write can trigger the stream again.

Bad:
DynamoDB update -> Stream -> Lambda -> Update same table -> Stream again

Better:
DynamoDB update -> Stream -> Lambda -> Write to another table or use guard conditions

Rule of thumb: use DynamoDB Streams when you need to react to item-level changes.

Amazon DocumentDB Change Streams

Amazon DocumentDB change streams can be used to process database changes from DocumentDB collections. This is useful when your application is document-oriented and needs reactive processing.

When to Use

  • Document database change processing
  • Audit trails
  • Search synchronization
  • Data replication workflows

Example Handler Idea

def lambda_handler(event, context):
    for record in event["events"]:
        print("DocumentDB change event:", record)

    return {
        "processed": len(event["events"])
    }

Rule of thumb: use DocumentDB change streams when DocumentDB is the source of truth and downstream systems need to react to changes.

Streaming Event Sources

Streaming event sources are designed for high-throughput ordered records. Lambda reads records in batches from streams and processes them by shard or partition.

Amazon Kinesis Data Streams

Kinesis Data Streams is used for real-time streaming data such as clickstream events, logs, telemetry, and analytics pipelines.

When to Use

  • Real-time analytics
  • Clickstream processing
  • IoT telemetry
  • Log pipelines
  • Ordered stream processing

Example Kinesis Event

{
  "Records": [
    {
      "eventSource": "aws:kinesis",
      "kinesis": {
        "partitionKey": "user_123",
        "data": "eyJldmVudCI6ICJjbGljayJ9"
      }
    }
  ]
}

Example Handler

import base64
import json

def lambda_handler(event, context):
    for record in event["Records"]:
        payload = base64.b64decode(record["kinesis"]["data"])
        data = json.loads(payload)

        print("Stream event:", data)

    return {
        "processed": len(event["Records"])
    }

Kinesis vs SQS

Need Better Choice
Simple background jobs SQS
Ordered stream processing Kinesis
Replayable stream records Kinesis
Task queue with retries and DLQ SQS

Rule of thumb: use Kinesis for streams, not simple job queues.

Amazon MSK

Amazon MSK is managed Apache Kafka. Lambda can consume records from Kafka topics through an event source mapping.

When to Use

  • Kafka-based event streaming
  • Existing Kafka architecture
  • High-throughput event pipelines
  • Event replay
  • Multiple consumer groups

Example Handler Idea

import base64
import json

def lambda_handler(event, context):
    for topic_partition, records in event["records"].items():
        for record in records:
            value = base64.b64decode(record["value"])
            data = json.loads(value)

            print("Kafka record:", data)

    return {
        "status": "processed"
    }

Rule of thumb: use MSK with Lambda when your event platform is Kafka and the consumer logic is suitable for Lambda execution limits.

Self-Managed Apache Kafka

Lambda can also consume from self-managed Apache Kafka. This is useful when Kafka runs outside MSK but you still want Lambda consumers.

When to Use

  • Existing self-managed Kafka clusters
  • Hybrid cloud architectures
  • Migration from custom consumers to Lambda
  • Serverless consumers for selected topics

Important Considerations

  • Networking must be configured correctly.
  • Authentication and TLS require careful setup.
  • Batch size and processing time must fit Lambda limits.
  • Operational ownership remains with your Kafka platform.

Rule of thumb: use self-managed Kafka integration only when Kafka is already part of your architecture.

Workflow Event Sources

Workflow event sources invoke Lambda as part of a larger process. Lambda is not the whole application; it is one task inside an orchestrated workflow.

AWS Step Functions

AWS Step Functions can call Lambda functions as steps in a state machine. This is useful for multi-step workflows, retries, branches, waits, and long-running business processes.

When to Use

  • Order processing
  • Approval workflows
  • ETL pipelines
  • Multi-step business processes
  • Workflows with retries and compensation

Example Workflow

Validate Order
  -> Reserve Inventory
  -> Charge Payment
  -> Send Confirmation
  -> Update Order Status

Example Lambda Step

def lambda_handler(event, context):
    order_id = event["orderId"]

    return {
        "orderId": order_id,
        "validationStatus": "valid"
    }

Rule of thumb: use Step Functions when the flow has multiple steps and the workflow itself must be observable.

AWS CodePipeline

AWS CodePipeline can use Lambda for custom actions in deployment workflows. Lambda can validate artifacts, run custom checks, notify systems, or integrate with tools not directly supported by CodePipeline.

When to Use

  • Custom deployment checks
  • Approval automation
  • Artifact validation
  • Custom notifications
  • Integration with external systems

Example Handler Idea

def lambda_handler(event, context):
    job_id = event["CodePipeline.job"]["id"]

    print("Processing CodePipeline job:", job_id)

    return {
        "jobId": job_id,
        "status": "success"
    }

Rule of thumb: use Lambda in CodePipeline when the deployment process needs custom logic.

Monitoring and Logging Event Sources

Monitoring and logging event sources invoke Lambda when operational data is produced. This is useful for security monitoring, alerting, log transformation, and automation.

Amazon CloudWatch Logs

CloudWatch Logs subscriptions can send log events to Lambda. This allows you to process logs in near real time.

When to Use

  • Security log scanning
  • Error detection
  • Log enrichment
  • Forwarding logs to external systems
  • Custom metrics extraction

Example Handler Idea

import base64
import gzip
import json

def lambda_handler(event, context):
    compressed_payload = base64.b64decode(event["awslogs"]["data"])
    uncompressed_payload = gzip.decompress(compressed_payload)
    log_data = json.loads(uncompressed_payload)

    for log_event in log_data["logEvents"]:
        print("Log message:", log_event["message"])

    return {
        "processed": len(log_data["logEvents"])
    }

Rule of thumb: use CloudWatch Logs subscriptions when logs themselves should trigger processing.

AWS Config

AWS Config can use Lambda for custom compliance rules. Lambda evaluates whether resources follow required policies.

When to Use

  • Custom compliance checks
  • Security governance
  • Resource configuration validation
  • Policy enforcement

Example Use Case

AWS Config detects resource configuration
Lambda evaluates compliance
Lambda returns COMPLIANT or NON_COMPLIANT

Rule of thumb: use AWS Config + Lambda when compliance logic is specific to your organization.

Event Source Comparison

Event Source Invocation Style Batching Typical Use Case Main Risk
API Gateway Synchronous No REST APIs Latency and timeout limits
Function URL Synchronous No Simple HTTP endpoints Limited API management
ALB Synchronous No Hybrid HTTP routing Response format and routing complexity
S3 Asynchronous Event records File processing Recursive triggers
SQS Poll-based Yes Background jobs Poison messages and retries
SNS Asynchronous Usually no Fan-out notifications Subscriber failure handling
EventBridge Asynchronous No Domain events and routing Event schema management
DynamoDB Streams Poll-based Yes Reacting to table changes Infinite update loops
Kinesis Poll-based Yes Streaming data Shard and retry management
Kafka / MSK Poll-based Yes Kafka event consumption Networking and consumer behavior
Step Functions Workflow-based No Multi-step workflows Workflow complexity
CloudWatch Logs Subscription-based Yes Log processing High log volume

Choosing the Right Event Source

The best event source depends on the problem you are solving. Do not choose a trigger only because it is easy to configure. Choose it based on delivery behavior, retry needs, ordering requirements, latency expectations, and failure handling.

If You Need Use Why
REST API API Gateway Routing, auth, throttling, custom domains
Simple webhook endpoint Function URL Fast setup and simple HTTPS endpoint
File processing S3 Events Runs when objects are created or changed
Background jobs SQS Durable queue, retries, DLQ
Broadcast to many consumers SNS Simple fan-out
Business domain events EventBridge Event bus and routing rules
React to DynamoDB changes DynamoDB Streams Item-level change events
High-throughput stream Kinesis or Kafka Ordered stream processing and replay
Multi-step process Step Functions Workflow orchestration
Scheduled task EventBridge Scheduler Cron-like invocation without servers

AWS Lambda Event Source Best Practices

Different event sources behave differently, but several best practices apply to almost all Lambda event-driven systems.

Idempotency

Idempotency means the same event can be processed more than once without corrupting data or creating duplicate side effects. This is critical because retries and duplicate deliveries can happen.

def process_payment(event):
    payment_id = event["paymentId"]

    if payment_already_processed(payment_id):
        return "already_processed"

    charge_customer(event)
    mark_payment_processed(payment_id)

    return "processed"

Retries and Dead Letter Queues

Always understand how your event source retries failed events. For queues and asynchronous systems, configure failure destinations or dead-letter queues where appropriate.

  • SQS: use a dead-letter queue for messages that fail repeatedly.
  • Async Lambda invocation: use destinations or DLQ for failed events.
  • Streams: handle poison records carefully because they can block shard progress.
  • HTTP APIs: return clear error responses and avoid retrying unsafe operations blindly.

Batch Processing

Stream and queue sources can send records in batches. Batching improves throughput, but failure handling becomes more important.

def lambda_handler(event, context):
    failed_items = []

    for record in event["Records"]:
        try:
            process_record(record)
        except Exception:
            failed_items.append({
                "itemIdentifier": record["messageId"]
            })

    return {
        "batchItemFailures": failed_items
    }

Rule of thumb: if one bad record should not fail the entire batch, use partial batch failure handling where supported.

Concurrency Management

Lambda can scale quickly, but downstream systems may not. Databases, APIs, and legacy systems can fail if too many Lambda executions run at the same time.

Problem:
10,000 queue messages
  -> many Lambda executions
  -> too many database connections

Better:
SQS batch size + reserved concurrency + RDS Proxy + backpressure

Event Filtering

Use event filtering when possible so Lambda receives only events it actually needs. This reduces cost, noise, and unnecessary invocations.

  • EventBridge rules can match specific event patterns.
  • S3 notifications can filter by prefix and suffix.
  • Event source mappings can filter some stream or queue events before invocation.

Monitoring and Logging

Event-driven systems can be hard to debug because one user action may produce many events. Use structured logs, correlation IDs, metrics, alarms, and tracing.

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(json.dumps({
        "requestId": context.aws_request_id,
        "eventSource": detect_event_source(event),
        "message": "Processing Lambda event"
    }))

def detect_event_source(event):
    if "Records" in event and event["Records"]:
        return event["Records"][0].get("eventSource", "unknown")

    return "unknown"

Conclusion

AWS Lambda event sources define how your serverless application behaves. The Lambda code is only one part of the design. The event source controls the shape of the input, invocation style, retry behavior, batching model, scaling behavior, and failure handling strategy.

Use API Gateway for REST APIs, Function URLs for simple endpoints, S3 events for file processing, SQS for background jobs, SNS for fan-out, EventBridge for domain events, DynamoDB Streams for database changes, Kinesis or Kafka for streaming, Step Functions for workflows, and EventBridge Scheduler for scheduled tasks.

Key takeaway: choosing the right Lambda event source is an architecture decision, not just a configuration detail. Good serverless systems are designed around clear event ownership, safe retries, idempotent processing, observability, and downstream limits.

More Articles to Read

Comments (0)