AWS AppSync for products with many client shapes, real-time needs, and data spread across several sources; Amazon API Gateway for plain HTTP consumers: public REST endpoints, webhooks, partner integrations described by OpenAPI. The real question is not "GraphQL or REST" but "who calls this API, and how much wire-level control do they need". On mature serverless platforms, running both side by side is the normal end state, not a compromise.
When does GraphQL earn its complexity?
GraphQL pays off when several clients need different shapes of the same data: a mobile app fetching a compact card, a web dashboard pulling the full record, an internal tool joining three sources. One schema, one endpoint, and each client requests exactly the fields it needs, which matters most on constrained mobile connections.
The other strong signal is aggregation. When a single screen combines data from DynamoDB, an RDS database, and a third-party API, a GraphQL layer resolves everything in one round trip instead of three client calls or a hand-built backend-for-frontend per view. If you have one web client with stable, predictable views, none of this applies: REST endpoints matched to those views stay easier to build, cache, and understand.
What does AppSync manage that you would otherwise build?
AppSync is GraphQL as a managed service: it hosts the schema, executes resolvers, and runs subscriptions over WebSocket without you operating a server. Resolvers are written in JavaScript and can call DynamoDB directly, no Lambda in the path, which removes a whole class of cold starts and glue code.
Subscriptions are the standout. Real-time fan-out to connected clients is built into the service, triggered by mutations in your schema. Server-side caching is available per resolver. A minimal direct-to-DynamoDB resolver looks like this:
// Unit resolver: DynamoDB directly, no Lambda in the path
import { util } from '@aws-appsync/utils';
export function request(ctx) {
return {
operation: 'GetItem',
key: util.dynamodb.toMapValues({ id: ctx.args.id }),
};
}
export function response(ctx) {
return ctx.result;
}
When does API Gateway stay the simpler choice?
API Gateway wins whenever the consumers are not your own frontends: webhook receivers, partner systems, vendor SDKs, anything scripted with curl. It gives you raw HTTP control (status codes, headers, content negotiation) and an OpenAPI contract you can export, version, and hand to a third party.
Usage plans with API keys handle per-partner throttling and quotas out of the box, a capability AppSync has no real equivalent for and one that matters as soon as you publish an SLA (we covered that side in partner API SLA design). For greenfield REST, compare the two API Gateway flavours first: HTTP APIs cover most needs with a lighter price shape than REST APIs.
Unsure whether your API layer should speak GraphQL, REST, or both? Describe your system: a one-page diagnosis within 48 hours.
Get my diagnosis →How do the cost shapes compare?
AppSync bills per query and mutation operation, with real-time billed separately per subscription update delivered and per connection-minute; optional caching adds an hourly instance charge. API Gateway bills per request, with HTTP APIs at a lighter rate than REST APIs, plus data transfer out.
The shapes matter more than the numbers. A chatty GraphQL client making many small queries pays per operation exactly the way a chatty REST client pays per request, so GraphQL only lowers the bill when it genuinely collapses round trips. Long-lived subscriptions cost connection-minutes even while idle. Check current figures and free-tier terms on the official pages for AppSync and API Gateway; both evolve over time.
AppSync vs API Gateway at a glance
Six dimensions separate the two services in practice. Protocol and real-time support are structural differences; auth and cost are configuration differences you can adapt either way. The last row is the one we use in audits: the ground where each service wins on its own terms, without forcing the other's use case.
| Dimension | AWS AppSync | Amazon API Gateway |
|---|---|---|
| Protocol | GraphQL over HTTP and WebSocket | HTTP/REST, plus raw WebSocket APIs |
| Real-time | Subscriptions built in, managed fan-out | WebSocket APIs: you manage connections and fan-out |
| Client flexibility | Each query picks fields and nesting | Fixed response shapes per endpoint |
| Auth | Cognito, OIDC, IAM, API keys, Lambda authorizers, per-field rules | IAM, Cognito, JWT, Lambda authorizers, usage plans with API keys |
| Cost shape | Per operation, plus per update and connection-minute for real-time | Per request (or per message and connection-minute on WebSocket) |
| When it wins | Many client shapes, mobile bandwidth, aggregation, live data | Webhooks, partner APIs, OpenAPI contracts, per-consumer quotas |
Schema-first discipline: the real cost of GraphQL
The operational price of AppSync is not the service, it is the schema. GraphQL forces a shared, versioned contract that every team touching the graph must respect: naming, nullability, pagination conventions, deprecation policy. Skip that discipline and you end up with a graph nobody dares to change.
Authorization also moves into the graph: per-field access rules replace per-endpoint ones, more precise and more work to test. Resolver design has its own failure mode too, the N+1 pattern, where a naive list resolver triggers one downstream call per item. All of this is solvable, but these are recurring engineering costs that REST simply never bills you for. Budget for them before choosing GraphQL, not after.
Why running both is the normal end state
Most platforms we see converge on a split: AppSync in front of first-party web and mobile clients, API Gateway for webhooks, partner endpoints, and machine-to-machine traffic. That is not architectural indecision, it is matching the protocol to the consumer.
Both sit on the same Lambda functions, the same DynamoDB tables, the same event bus, so the duplication is thinner than it looks: two front doors, one backend. The mistake worth avoiding is forcing one service into the other's role, a GraphQL mutation pretending to be a webhook receiver, or a REST endpoint re-implementing field selection through query parameters. If that split is exactly what you are designing now, it is the core of our API development service.
Decision checklist
- Three or more distinct client shapes on the same data: AppSync.
- Mobile clients on constrained networks: AppSync.
- Live updates (chat, dashboards, presence): AppSync subscriptions.
- Webhooks, partner integrations, OpenAPI contracts: API Gateway.
- Per-consumer quotas and API keys: API Gateway usage plans.
- One web client with stable views: API Gateway (HTTP API), keep it simple.
- Both profiles present: run both, one backend behind them.
When in doubt, start with REST: adding GraphQL later, once client shapes multiply, is far easier than removing it.