Overcast is alpha — behaviour and APIs may change between releases. Pin your version and read the changelog before upgrading.

overcast

AppSync — AWS AppSync

Quick start, the GraphQL and realtime endpoint URLs, the data sources and authorization modes that execute for real, and the ones that are only stored.

GraphQL APIs execute: schemas are parsed and validated, resolvers run VTL or APPSYNC_JS against real data sources, and mutations fan out to live subscriptions.

Status: ✅ Supported

Quick start

export AWS_ENDPOINT_URL=http://localhost:4566
API=$(aws appsync create-graphql-api --name demo --authentication-type API_KEY \
  --query graphqlApi.apiId --output text)
KEY=$(aws appsync create-api-key --api-id "$API" --query apiKey.id --output text)

printf 'type Query { hello: String }' > schema.graphql
aws appsync start-schema-creation --api-id "$API" --definition fileb://schema.graphql
aws appsync create-data-source --api-id "$API" --name none --type NONE
aws appsync create-resolver --api-id "$API" --type-name Query --field-name hello \
  --data-source-name none \
  --request-mapping-template '{"version":"2018-05-29","payload":"world"}' \
  --response-mapping-template '$util.toJson($ctx.result)'

curl -s "http://localhost:4566/_overcast/appsync/apis/$API/graphql" \
  -H "x-api-key: $KEY" -d '{"query":"{ hello }"}'

Any credentials work; with none configured, run eval "$(overcast env)" first — see Using AWS SDKs and CLI.

Endpoint URLs

A GraphQL API answers on two forms, with identical behaviour:

FormGraphQLSubscriptions
Path-style{base}/_overcast/appsync/apis/{apiId}/graphql{base}/_overcast/appsync/apis/{apiId}/realtime
Host-routedhttp://{apiId}.appsync-api.{region}.{base}/graphqlhttp://{apiId}.appsync-realtime-api.{region}.{base}

uris reports the host-routed form — what Fn::GetAtt GraphQLUrl passes on — whenever the hostname you reached Overcast on can carry a subdomain. On a bare localhost or an IP it reports the path-style form. Set OVERCAST_HOSTNAME=localhost.overcast.sh to get the AWS shape everywhere — see Hostnames that resolve for every caller.

The dns map always carries host-routed names — it holds a name, not a URL, so it has nothing else to carry. dns.REALTIME repeats dns.GRAPHQL’s host: Overcast serves both endpoints from one place.

What works

AreaBehaviour
SchemaSDL parsed and validated on upload; a schema without a Query type is rejected with 400
ResolversUNIT and PIPELINE, VTL and APPSYNC_JS, with $util/util including dynamodb, http, str, time, math, transform
Data sourcesNONE, HTTP, AWS_LAMBDA and AMAZON_DYNAMODB resolve at execution time, including DynamoDB batch and transact operations
ExecutionMulti-operation documents via operationName, field arguments, nested field resolution, introspection, fragments
SubscriptionsAppSync’s real-time WebSocket protocol, with mutation fan-out by convention: createFoo notifies onCreateFoo
AuthorizationAPI keys with expiry, and Lambda authorizers for real — invoked per request, honouring isAuthorized, identityValidationExpression, deniedFields and the result TTL. OVERCAST_ENFORCE_APPSYNC_COGNITO_AUTH=true additionally verifies Cognito bearer tokens against the local user pool
Merged APIsSource API association merges schemas; StartSchemaMerge re-merges on demand
Events APIEvent APIs and channel namespaces under /v2/apis, sharing the path with API Gateway v2
CloudFormationGraphQL API, schema, key, data source, resolver, function, domain name, cache, source-API association, Event API and channel namespace resources
EvaluationEvaluateCode and EvaluateMappingTemplate need no API to exist

Differences from AWS

AreaOn AWSOvercast
Cognito authorizationThe JWT signature and expiry are verifiedA bearer token is required and its claims are decoded; the signature, issuer, token_use, expiry and appIdClientRegex are verified only under OVERCAST_ENFORCE_APPSYNC_COGNITO_AUTH=true, and only against a local Cognito pool
OIDC authorizationThe JWT signature and expiry are verifiedA bearer token is required and its claims are decoded, but the signature and expiry are not verified
IAM authorizationThe SigV4 signature is verifiedAccepted unconditionally; the signature is not verified
Other data source typesOpenSearch, Elasticsearch, RDS, EventBridge and Bedrock RuntimeStored, but a resolver against one fails at execution
API cacheCreateApiCache provisions a cacheIt stores configuration; nothing is cached
Domain namesReal DNS and certificate validationSynthetic appsyncDomainName and hostedZoneId; no DNS or certificate validation
Complex configsValidated and appliedlogConfig, userPoolConfig, openIDConnectConfig and similar are stored as passthrough JSON
outErrorsCarries util.appendError outputNever populated by either evaluator — that output is not collected

The full list is in AppSync limitations.

Cognito authorization: relaxed by default

AMAZON_COGNITO_USER_POOLS accepts any bearer token by default and decodes its payload into $context.identity, so a resolver test can mint an unsigned JWT without standing up a user pool.

Set OVERCAST_ENFORCE_APPSYNC_COGNITO_AUTH=true to make the emulator verify the token instead. It must then be one Overcast’s own Cognito minted for the user pool named in the API’s userPoolConfig.userPoolId, and it must pass every check AWS applies:

CheckedRejected when
Presence and shapeNo Authorization: Bearer <jwt> header, or a payload that will not decode
Issueriss does not name the API’s configured pool. A token from any other pool — or from an issuer that is not a local pool at all — is refused
token_useNeither id nor access. Both work, as on AWS; the ID token is what Amplify sends
SignatureThe RS256 signature does not match the pool’s key
Expiryexp has passed
appIdClientRegexConfigured, and the token’s aud (ID token) or client_id (access token) does not match it

Every failure is AppSync’s UnauthorizedException with HTTP 401, carried in the GraphQL error envelope (errors[0].errorType), not the AWS JSON error envelope other Overcast services use. The rule applies identically to the API’s primary authorization mode and to any Cognito entry in additionalAuthenticationProviders.

Verification runs in-process against the local Cognito service — no network call, and no JWKS to configure. Tokens from real AWS Cognito are refused, because Overcast has no key for them.

Gotchas

Caution

By default, Cognito, OIDC and IAM authorization are presence checks, not verification: a forged or expired token is accepted. A Lambda authorizer is executed here, and Cognito can be switched to real verification (above); OIDC and IAM authorization are only ever proven on AWS.

Failures inside a resolver surface in the GraphQL response, not only in logs.

Tip

$util.error errorType and data are propagated into extensions.errorType/extensions.data, and a field resolver error carries a path array. Read the extensions block before adding logging.

Operations

All 81 listed operations are implemented. Per-operation status, notes and AWS API links: AppSync operations.