đŸAppSync for GraphQL: When it actually shinesđŸ
đ€ âAppSync is for GraphQLâ seems like obvious advice. Yet I keep seeing teams either implement GraphQL on API Gateway + Lambda, or worse â regret their AppSync choice six months in.
The typical framing is: âDo you need GraphQL? Use AppSync.â But the actual question should be: âWhat are you optimizing for, and what operational complexity can your team handle?â Let me show you where AppSync genuinely excels â and the hidden costs.
â
When AppSync shines: Real-time subscriptions
AppSync subscriptions are transformative for real-time updates. They handle WebSocket connections, message filtering, and fan-out automatically.
type Subscription {
onOrderStatusChange(customerId: ID!): Order
@aws_subscribe(mutations: [âupdateOrderStatusâ])
}With API Gateway, youâd build this yourself: WebSocket APIs + DynamoDB connection tracking + Lambda broadcasting. Thatâs 3-4 services vs AppSyncâs built-in handling.
The cost reality: WebSocket connections cost money even when idle.
For 10,000 users connected 8 hours/day (rough estimate):
Connections: $0.08/million minutes = ~$384/day
Messages: $1.00/million messages
If your âreal-timeâ is actually âpoll every 30 seconds,â REST might be cheaper.
â
When AppSync shines: Multi-source data federation
One GraphQL query, multiple data sources in parallel:
type User {
profile: UserProfile # DynamoDB
orders: [Order] # RDS Aurora
recommendations: [Product] # Lambda + ML
}AppSync orchestrates this automatically. With API Gateway + Lambda, you write the orchestration logic yourself.
The N+1 problem: Watch out for this query:
{
users(limit: 100) {
orders # 100 separate DB calls!
}
}AppSync makes 1 call for users, then 100 calls for orders. You need DataLoader patterns or batch resolvers to fix this.
â
When AppSync shines: Schema-first discipline
AppSync forces you to maintain a .graphql schema file as your API contract:
Frontend teams work from schema immediately
Breaking changes are explicit
Auto-generated documentation via GraphiQL
Prevents ânobody knows what the API actually doesâ syndrome
This is less about AppSync features and more about enforced discipline that pays off.
â
When AppSync shines: Field-level authorization
AppSyncâs authorization is underrated. You get four modes (API Key, IAM, Cognito, OIDC) that can be applied per-field:
type User {
id: ID!
email: String @aws_auth(cognito_groups: [âUsersâ])
salary: Float @aws_auth(cognito_groups: [âAdminsâ])
}Same type, different field-level permissions. With API Gateway, youâd implement this in every Lambda resolver manually. AppSync supports multiple auth modes per API natively. Some fields use Cognito, others use IAM, others use API keys â all in one schema.
The limitation: Authorization is tied to Cognito groups or IAM policies. Custom RBAC (permissions stored in your DB) still needs Lambda resolver logic.
â When AppSync disappoints: The VTL reality
AppSync promises âno Lambda neededâ via VTL (Velocity Template Language) resolvers:
{
âversionâ: â2017-02-28â,
âoperationâ: âQueryâ,
âqueryâ: {
âexpressionâ: âPK = :pkâ,
âexpressionValuesâ: {
â:pkâ: $util.dynamodb.toDynamoDBJson($ctx.args.userId)
}
}
}This works for simple CRUD. But add:
Business logic (discounts, access control)
External API calls with retries
Complex data transformations
Any
if/elsebeyond basics
Youâre writing Lambda anyway. Now you maintain VTL templates AND Lambda code.
Reality check: In every production AppSync API Iâve observed, 60%+ of resolvers use Lambda. The âno Lambdaâ promise rarely survives contact with real requirements.
Update (late 2022): AWS introduced APPSYNC_JS runtime as a VTL replacement. You can now write resolvers in JavaScript. Itâs still not full Node.js. No npm packages, limited standard library. Async/await works for AppSync operations, but external HTTP calls need Lambda anyway. For complex business logic, youâre still reaching for Lambda. But for data access patterns, APPSYNC_JS is a significant improvement over VTL.
đProduction gotchas:
Pipeline resolvers: use these to chain multiple logic steps (e.g., Auth â Validation â DB call) without repeating code. They turn fragmented VTL/JS into a reusable sequence. Powerful but under-documented.
HTTP resolvers: allow AppSync to act as a GraphQL Gateway. You can proxy existing REST or even external GraphQL APIs directly, merging legacy services into your new graph.
Caching: AppSync now offers two layers. Resolver-level caching is the default (5 resolvers check cache 5 times), but Operation-level caching (new in 2025) finally allows you to cache the entire query response natively.
256KB request limit: a hard limit for the total request (payload + headers). While API Gateway handles 10MB, AppSync requires you to offload large data or file uploads to S3 using pre-signed URLs.
VTL has no debugger: Unlike API Gateway, there is no true local runtime. You can use basic simulators like Amplify Mock, but for complex logic, you are stuck with $util.log() and CloudWatch. Local testing remains the biggest developer experience bottleneck.
Thank you for reading, letâs chat đŹ
đŹ Have you tried AppSync in production?
đŹ Do you use APPSYNC_JS or still stuck with VTL?
đŹ Whatâs your biggest pain point with GraphQL?
I love hearing from readers đ«¶đ» Please feel free to drop comments, questions, and opinions belowđđ»




Great breakdown of AppSync's real tradeoffs. The point about 60% of resolvers ending up in Lambda anyway is spot-on from what I've seen, teams often start with VTL thinking they'll stay "serverless" but busiess logic always creeps in. The WebSocket cost calculaton for real-time features is especially helpful since most tutorials skip that part. Honestly saved me from over-architecting a polling system that would've been cheaper.