AppSync Foundations: Schema, Unit Resolvers & Direct Data Sources
AppSync is not 'GraphQL with Lambda underneath' — it is a managed execution engine where resolvers connect directly to data sources. This article covers schema design with SDL, unit resolver architecture with the APPSYNC_JS runtime, connecting resolvers directly to DynamoDB and HTTP endpoints without Lambda, and the auth modes available on an AppSync API.
AWS Serverless Engineering: Lambda to Production
AppSync Foundations: Schema, Unit Resolvers & Direct Data Sources
Every AWS primitive is a tradeoff surface, not a feature toggle. A common AppSync architecture wires every GraphQL mutation and query to a Lambda resolver — because tutorials do, and because "Lambda handles everything" feels safe. The hidden cost: every GetUser query now pays a cold-start risk, Lambda invocation billing, and ~50ms minimum added latency for a resolver whose entire function body is dynamoDB.getItem() and return item. AppSync's managed resolver model exists precisely to eliminate this pattern. For the majority of CRUD operations, the correct data source is DynamoDB connected directly — no Lambda in the path.
Series boundary: AppSync is a managed GraphQL execution engine distinct from self-hosted GraphQL runtimes. This article covers AppSync exclusively. For self-hosted schema design, N+1 DataLoader patterns, and Apollo Federation, see the GraphQL Backend & API Design series.
1. What AppSync Is (and Is Not)
AppSync is not an API Gateway for GraphQL — it is a fully managed GraphQL execution engine with built-in resolver execution, real-time WebSocket subscription management, multiple auth mode support, and a caching layer. The closest analogy: AppSync is to GraphQL what DynamoDB is to NoSQL — a managed service that handles the infrastructure concerns so you write only business logic.
1.1 When to Choose AppSync over API Gateway + Lambda
| Use case | AppSync | API Gateway + Lambda |
|---|---|---|
| Real-time subscriptions (WebSocket) | ✅ Managed — no WebSocket server code | ❌ Requires WebSocket API + DynamoDB connection tracking |
| Multi-source resolver per field | ✅ Pipeline resolvers compose multiple sources | ❌ Lambda must orchestrate manually |
| GraphQL schema validation | ✅ Built-in SDL validation + execution | ❌ Apollo/Pothos setup required |
| Managed auth modes (Cognito, API key, IAM) | ✅ Configurable per operation | ❌ Must implement in Lambda |
| REST API alongside GraphQL | ❌ GraphQL only | ✅ REST routes supported |
| Complex business logic per request | Depends — Lambda data source for complex cases | ✅ Full Node.js/Python/etc. runtime |
2. Schema Definition Language (SDL)
The SDL is the contract between your API and all consumers. Every breaking change in the SDL is a breaking change for every client that depends on it.
2.1 Types and Fields
Non-null declarations (!) are a client contract enforced by AppSync. If a resolver returns null for a non-null field, AppSync propagates null upward through the response tree until it reaches a nullable ancestor — which may null out the entire response object. Use non-null only for fields your resolver can unconditionally return. Marking a field non-null and then returning null from the resolver is a runtime error, not a compile error.
2.2 Operations
2.3 AppSync Custom Scalars
AppSync provides built-in scalar types beyond the GraphQL specification defaults:
| Scalar | Format | Use case |
|---|---|---|
AWSDateTime |
ISO 8601 with timezone | Timestamps |
AWSDate |
YYYY-MM-DD |
Date-only fields |
AWSTime |
HH:mm:ss |
Time-only fields |
AWSTimestamp |
Unix epoch (integer) | Machine timestamps |
AWSJSON |
JSON string | Untyped JSON blobs |
AWSEmail |
RFC 5321 email | Email fields with validation |
AWSURL |
RFC 3986 URL | URLs |
AWSPhone |
E.164 phone | Phone numbers |
AWSIPAddress |
IPv4/IPv6 | IP address fields |
3. The APPSYNC_JS Runtime
APPSYNC_JS is a JavaScript execution environment running in a V8 sandbox. Resolvers written in APPSYNC_JS replace the legacy VTL mapping template syntax. Each resolver has two exported functions:
request(ctx)— receives the GraphQL context, returns the operation to perform on the data sourceresponse(ctx)— receives the data source result, returns the transformed GraphQL response
4. Unit Resolvers — Connecting Fields to Data Sources
A unit resolver maps a single GraphQL field to a single data source. It has one request() function and one response() function. It connects to exactly one data source per invocation.
4.1 DynamoDB Unit Resolver — GetItem
4.2 DynamoDB Unit Resolver — PutItem (Mutation)
4.3 Lambda Unit Resolver — When Lambda Is Correct
Use a Lambda data source when the resolver requires:
- External API calls (payment gateway, email service, third-party REST API)
- Complex business logic that cannot run in the APPSYNC_JS sandbox
- Database operations against RDS, Elasticsearch, or other non-DynamoDB stores
4.4 HTTP Data Source Resolver
For external REST API calls without Lambda:

5. Authorization Modes
AppSync supports four authorization modes per API, configurable per operation:
| Mode | How it works | Use case |
|---|---|---|
| API Key | Static key in x-api-key header |
Public read-only data; short-lived demos |
| Cognito User Pool | JWT from Cognito User Pool | User-facing apps with Cognito identity |
| IAM | AWS SigV4 signature | Machine-to-machine; AWS service callers |
| Lambda | Custom auth Lambda function | Third-party tokens; complex auth logic |
| OIDC | JWT from any OIDC provider | Non-Cognito identity (Auth0, Okta) |
Inside resolvers, ctx.identity provides the caller's auth context:

6. Deploying AppSync with CDK
Summary
| Concept | Rule |
|---|---|
| AppSync is a managed execution engine | Not a Lambda router for GraphQL — resolvers connect to data sources directly |
| Unit resolver | One field, one data source, one request() / response() function pair |
| APPSYNC_JS sandbox | No fetch(), no npm packages, no async/await — pure transformation only |
| Non-null SDL fields | A client contract enforced at runtime — returning null for a non-null field propagates null upward |
| Lambda data source | Use only when the operation requires network calls, external APIs, or complex business logic |
| Auth via ctx.identity | Caller's Cognito claims, IAM principal, or custom Lambda auth context available in every resolver |
What's Next
In Part 6: AppSync Advanced — Pipeline Resolvers, Subscriptions & Multi-Auth, we compose multi-step operations with pipeline resolvers, build real-time subscriptions with server-side event filtering, and enforce field-level authorization in schemas with multiple simultaneous auth modes.
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.