Beyond Polling: Building Event-Driven API Gateways with AWS AppSync and EventBridge
Learn how to replace resource-heavy polling mechanisms in client applications by pairing GraphQL Subscriptions in AWS AppSync with Amazon EventBridge for real-time, event-driven updates.
Building modern, responsive user experiences often requires streaming real-time updates to front-end clients—whether it's live status dashboards, order tracking, or collaborative tools. A common architectural anti-pattern is relying on client-side short or long polling against REST endpoints. This approach wastes network bandwidth, scales poorly, and increases backend serverless compute costs.
Combining Amazon EventBridge and AWS AppSync offers a fully serverless, highly scalable pattern to push real-time updates to clients without managing WebSocket infrastructure or constantly polling APIs.
The Architecture Overview
Instead of client apps hitting an API Gateway endpoint every few seconds, the workflow functions as follows:
- State Change Event: An upstream service (e.g., AWS Lambda, Step Functions, or DynamoDB Streams) publishes a custom event to an Amazon EventBridge event bus.
- Event Routing: EventBridge filters and routes the target event directly to AWS AppSync using the EventBridge API destination or HTTP target integration.
- Real-Time Push: AWS AppSync invokes a local resolver that triggers a GraphQL Mutation. AppSync automatically broadcasts the data over established WebSocket connections to subscribed clients.
Step 1: Setting Up the GraphQL Schema in AWS AppSync
Define a schema that includes the data payload, a mutation to receive event updates, and a subscription for real-time client listeners.
type OrderStatus {
orderId: ID!
status: String!
updatedAt: String!
}
orderId: ID!
status: String!
updatedAt: String!
}
type Query {
getOrderStatus(orderId: ID!): OrderStatus
}
getOrderStatus(orderId: ID!): OrderStatus
}
type Mutation {
publishStatusUpdate(orderId: ID!, status: String!, updatedAt: String!): OrderStatus!
}
publishStatusUpdate(orderId: ID!, status: String!, updatedAt: String!): OrderStatus!
}
type Subscription {
onOrderStatusUpdated(orderId: ID!): OrderStatus
@aws_subscribe(mutations: ["publishStatusUpdate"])
}
onOrderStatusUpdated(orderId: ID!): OrderStatus
@aws_subscribe(mutations: ["publishStatusUpdate"])
}
Step 2: Configuring a Local Resolver
To avoid unnecessary database writes when broadcasting updates, attach a None/Local Data Source to the
publishStatusUpdate mutation field.Request Mapping Template (VTL):
{
"version": "2018-05-29",
"payload": {
"orderId": "$context.arguments.orderId",
"status": "$context.arguments.status",
"updatedAt": "$context.arguments.updatedAt"
}
}
"version": "2018-05-29",
"payload": {
"orderId": "$context.arguments.orderId",
"status": "$context.arguments.status",
"updatedAt": "$context.arguments.updatedAt"
}
}
Response Mapping Template (VTL):
$util.toJson($context.result)
Step 3: Routing Events from EventBridge to AppSync
To trigger the
publishStatusUpdate GraphQL mutation from Amazon EventBridge:- Create an API Destination in EventBridge pointing to your AWS AppSync GraphQL endpoint.
- Add an authorization header using your AppSync API key or IAM credentials.
- Define an EventBridge Rule targeting the API Destination with a custom JSON transformer that structures the GraphQL payload:
{
"query": "mutation PublishStatusUpdate($orderId: ID!, $status: String!, $updatedAt: String!) { publishStatusUpdate(orderId: $orderId, status: $status, updatedAt: $updatedAt) { orderId status updatedAt } }",
"variables": {
"orderId": "<$.detail.orderId>",
"status": "<$.detail.status>",
"updatedAt": "<$.detail.timestamp>"
}
}
"query": "mutation PublishStatusUpdate($orderId: ID!, $status: String!, $updatedAt: String!) { publishStatusUpdate(orderId: $orderId, status: $status, updatedAt: $updatedAt) { orderId status updatedAt } }",
"variables": {
"orderId": "<$.detail.orderId>",
"status": "<$.detail.status>",
"updatedAt": "<$.detail.timestamp>"
}
}
Benefits of This Pattern
- Zero Infrastructure Overhead: No persistent WebSocket server fleets to provision or scale.
- Cost Efficiency: Reduces unnecessary HTTP GET requests from clients by broadcasting updates only when state changes occur.
- Decoupled Architecture: Upstream domain services simply publish events to EventBridge without needing to know which client clients or APIs consume them.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article