Skip to content

Intermediate · Serverless, monitoring and cost control · topic 20 of 50 · 30 min

Event-driven design: API Gateway, SQS, SNS, EventBridge and Step Functions

Explanation

In an event-driven system, parts talk by leaving messages instead of calling each other and waiting. API Gateway is the front door, SQS is a queue that holds work until a worker is ready, SNS copies one message to many subscribers, EventBridge routes events and runs schedules, and Step Functions runs multi-step workflows. Decoupling like this lets each part fail, retry and scale on its own.

Why decouple?

Imagine a food delivery app where the order API does everything itself, one step after another: save the order, charge the card, tell the restaurant, send an SMS, update the analytics. If the SMS provider is slow, the customer stares at a spinner. If analytics is down, the whole order fails. The parts are tightly coupled: each one depends on all the others being up and fast right now.

Decoupling means putting a message in between. The API saves the order, drops a message saying "order placed" into a queue, and answers the customer at once. Workers pick up the message when they are ready. If a worker crashes, the message waits and is tried again. Each part can be scaled, changed or fixed without touching the others.

Think of it like this: Think of a busy dhaba. The waiter writes the order on a slip and clips it to the kitchen rail, then goes back to the tables. He does not stand next to the cook waiting. The rail is the queue: if the cooks are busy, slips wait; if one slip falls, someone picks it up again.

API Gateway: the front door

Amazon API Gateway gives your backend a public HTTPS address, checks requests, and passes them to Lambda or another service. It comes in three types: REST APIs, HTTP APIs and WebSocket APIs.

REST APIHTTP API
FeaturesMore: API keys and usage plans, request validation, caching, AWS WAF, private APIsFewer, simpler: JWT authorisers, CORS, Lambda and HTTP backends
Price and speedHigher price per million requestsCheaper and lower latency
Choose it whenYou need one of its extra featuresYou want a simple, low-cost API in front of Lambda

SQS: queues

Amazon SQS (Simple Queue Service) stores messages until a consumer reads them. A producer sends a message; a consumer polls the queue, processes the message, and then deletes it. If the consumer never deletes it, SQS assumes the work failed.

  • Standard queues: almost unlimited throughput, at-least-once delivery (a message can occasionally arrive twice) and best-effort order.
  • FIFO queues: strict order within a message group and exactly-once processing within a deduplication window. The name must end in .fifo, and throughput is lower.

The visibility timeout is the key idea. When a consumer receives a message, SQS hides it from other consumers for that many seconds (30 by default, up to 12 hours). If the consumer deletes it in time, it is gone. If not, it becomes visible again and another consumer gets it. With Lambda reading the queue, AWS recommends a visibility timeout of at least six times the function timeout.

A message that keeps failing is called a poison message. A dead-letter queue (DLQ) catches it: in the source queue's redrive policy you set maxReceiveCount, say 5. After a message has been received 5 times without being deleted, SQS moves it to the DLQ, where a human can inspect it and later redrive it back.

json
{
  "VisibilityTimeout": "60",
  "MessageRetentionPeriod": "345600",
  "RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:ap-south-1:111122223333:orders-dlq\",\"maxReceiveCount\":\"5\"}"
}
orders-queue.json: queue attributes. Retention 345,600 seconds is 4 days, the default. The redrive policy is JSON written inside a string.
bash
aws sqs create-queue --queue-name orders-dlq
aws sqs create-queue --queue-name orders --attributes file://orders-queue.json
aws sqs send-message --queue-url https://sqs.ap-south-1.amazonaws.com/111122223333/orders \
  --message-body '{"orderId":"A1001","city":"Pune"}'
aws sqs receive-message --queue-url https://sqs.ap-south-1.amazonaws.com/111122223333/orders \
  --wait-time-seconds 20
Create the DLQ first, then the main queue. --wait-time-seconds 20 turns on long polling, which waits for messages instead of returning empty at once.

SNS: publish once, deliver to many

Amazon SNS (Simple Notification Service) is publish/subscribe. A publisher sends one message to a topic; SNS delivers a copy to every subscriber: SQS queues, Lambda functions, HTTPS endpoints, email or SMS. This is called fan-out. Subscribers can use filter policies to receive only the messages they care about. A common pattern is SNS in front of several SQS queues, so each subscriber gets its own durable copy.

EventBridge: an event router and scheduler

Amazon EventBridge has event buses. AWS services, your own apps and some SaaS partners put events on a bus. Rules match events by pattern and send them to targets such as Lambda, SQS or Step Functions. EventBridge Scheduler runs targets on a cron or rate schedule, with time zone support, which replaces many old "cron on a server" jobs.

json
{
  "source": ["foodapp.orders"],
  "detail-type": ["OrderPlaced"],
  "detail": { "city": ["Pune", "Mumbai"] }
}
An EventBridge event pattern: match OrderPlaced events from the orders app, only for Pune and Mumbai.

Step Functions: workflows

AWS Step Functions runs a state machine: a workflow of steps written in JSON (Amazon States Language). Each step can call Lambda or another AWS service, with built-in retries, error catching, branches, waits and parallel steps. Standard workflows can run for up to a year; Express workflows are for short, high-volume runs of up to 5 minutes.

Putting it together: an order on a food delivery app

  1. The app sends POST /orders to API Gateway, which calls a small Lambda function.
  2. That function checks the order, saves it to DynamoDB, sends a message to the orders SQS queue, and answers 202 Accepted within a fraction of a second.
  3. A worker Lambda reads the queue in batches, charges the payment and updates the order status.
  4. When payment succeeds, the worker publishes OrderConfirmed to an SNS topic. Subscribers: a queue for the restaurant tablet service, a function that sends the SMS and push notification, and a queue for analytics.
  5. A message that fails 5 times goes to the DLQ, and a CloudWatch alarm on the DLQ size tells the on-call engineer.
Careful: Common mistakes: (1) A visibility timeout shorter than the processing time, so a second worker picks up the same order while the first is still working. (2) No DLQ, so a poison message is retried again and again until its retention period ends. (3) Consumers that are not idempotent: with standard queues a message can arrive twice, so use the order ID to make sure a card is never charged twice. (4) Sending SNS straight to an HTTPS endpoint that is often down; put an SQS queue in between for durability.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Event-driven design: API Gateway, SQS, SNS, EventBridge and Step Functions · 1/8

Event-driven design

This lesson is about event-driven design, where parts of a system talk through messages instead of waiting on each other.

Prefer a video by a teacher? Find free videos on “aws sqs sns eventbridge explained decoupling dead letter queue” on YouTube ↗

In real life: Weekend rush at a Bengaluru food delivery startup

On Saturday nights orders jump to ten times the weekday level. Because the order API only saves the order and drops a message on an SQS queue, it answers customers quickly even when the payment provider slows down. The worker functions scale up with the queue length. When one restaurant's tablet integration broke, its messages piled up in the DLQ instead of blocking everyone else, and an alarm on the DLQ reached the on-call engineer within five minutes. After the fix she redrove the messages back to the main queue and no order was lost.

python
import json

def worker(event, context):
    for record in event["Records"]:
        order = json.loads(record["body"])
        if already_processed(order["orderId"]):   # idempotency check
            continue
        charge_payment(order)
        mark_processed(order["orderId"])
A worker reading SQS batches. The idempotency check stops double charging when a message arrives twice.

Practice (0/3 done)

Task 1 · Question

One "order confirmed" message must reach the restaurant service, the SMS service and analytics, each at its own pace. Which pattern fits best?

Task 2 · Code · Python

Simulate an SQS queue with a visibility timeout and a DLQ. Write run_queue(messages, failures, visibility_timeout, max_receive_count). All messages arrive at second 0. One worker works one message per second: at each second t (0, 1, 2 ...) it receives the visible message (visible_at <= t) with the smallest visible_at, ties broken by the original order. Receiving adds 1 to that message's receive count. failures[id] says how many times processing fails before it succeeds (missing means 0). If it fails: when the receive count has reached max_receive_count, move the message to the DLQ; otherwise it becomes visible again at t + visibility_timeout. If it succeeds, it is deleted and (id, t) is added to done. If nothing is visible, the worker just waits for the next second. Stop when the queue is empty and return (done, dlq).

Task 3 · Practice

Design the event flow for a college canteen pre-order app: students order from their phones, the kitchen screen shows new orders, the student gets an SMS when food is ready, and the canteen manager gets a daily sales report at 9 pm. Name the AWS service for each step and explain why.

Quick quiz

Question 1

1.A worker received an SQS message but crashed before deleting it. What happens?

Question 2

2.Bank transactions for one account must be processed strictly in order. Which queue type?

Question 3

3.What is a dead-letter queue for?

Question 4

4.Which service runs a Lambda function every weekday at 9 am Indian time?

Question 5

5.Why must consumers of a standard SQS queue be idempotent?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A worker received an SQS message but crashed before deleting it. What happens?

Answer: After the visibility timeout ends, the message becomes visible again

Messages are only removed when deleted. When the visibility timeout ends, another consumer can receive it. It goes to the DLQ only after maxReceiveCount receives.

Bank transactions for one account must be processed strictly in order. Which queue type?

Answer: SQS FIFO queue with the account number as the message group ID

FIFO queues keep strict order within a message group, so grouping by account keeps each account in order.

What is a dead-letter queue for?

Answer: Holding messages that failed processing too many times

After maxReceiveCount failed receives, SQS moves the message to the DLQ so it stops blocking work and can be inspected.

Which service runs a Lambda function every weekday at 9 am Indian time?

Answer: EventBridge Scheduler

EventBridge Scheduler runs targets on cron or rate schedules and supports time zones like Asia/Kolkata.

Why must consumers of a standard SQS queue be idempotent?

Answer: Standard queues may deliver a message more than once

Standard queues give at-least-once delivery, so the same message can arrive twice. Processing it twice must be harmless.

In a queue system, what is a worker?Related

Answer: A process that takes jobs from the queue and does them

Producers add jobs; workers (consumers) take them from the queue and run them.

From Background jobs and queues

Why put "order.placed" events on a queue?Related

Answer: So the API answers fast and workers handle slow jobs reliably

The queue holds the work safely; the API does not wait for SMS, analytics and other slow tasks.

From System design: planning a big backend

What is a good name for an event raised after a payment fails?Related

Answer: PaymentFailed

Events are usually named in the past tense.

From Events: letting objects notify each other

What gives a Lambda function permission to read from a DynamoDB table?Related

Answer: Its execution role

The execution role is the IAM role Lambda assumes when running your code. Its policy must allow the DynamoDB read.

From Serverless with AWS Lambda

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Serverless, monitoring and cost control” before its exam: Monitoring with CloudWatch, CloudTrail and Config, Cost control in practice.
  2. Module 5 exam: Serverless, monitoring and cost control · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Microsoft Azure essentials. 50 topics left in the course before the final exam.

← Previous: Serverless with AWS Lambda