Skip to content

AppSync

Protocol: REST JSON Endpoint: http://localhost:4566/v1/apis/...

Floci implements the AWS AppSync Management API, providing local emulation of GraphQL API configuration, schema management, data source binding, resolver mapping, API key provisioning, custom domains, and channel namespaces.

OIDC issuer network policy

AppSync OIDC authentication uses the shared JWT issuer policy. By default, issuer discovery and JWKS requests require HTTPS and reject local, private, link-local, and other non-public addresses. For an isolated development environment, set FLOCI_SECURITY_ALLOW_PRIVATE_JWT_TARGETS=true. This also applies to API Gateway HTTP API JWT authorizers. The option permits private HTTPS targets and HTTP URLs that use a literal private or loopback address.

Supported Operations

GraphQL API

Operation Description
CreateGraphqlApi Create a GraphQL API
GetGraphqlApi Get a GraphQL API by ID
UpdateGraphqlApi Update a GraphQL API
DeleteGraphqlApi Delete a GraphQL API and all child resources
ListGraphqlApis List all GraphQL APIs

Schema

Operation Description
StartSchemaCreation Start schema creation — validates and parses SDL using graphql-java (invalid SDL returns 400)
GetSchemaCreationStatus Get schema creation status
GetIntrospectionSchema Get the introspection schema

Data Sources

Operation Description
CreateDataSource Create a data source
GetDataSource Get a data source by name
UpdateDataSource Update a data source
DeleteDataSource Delete a data source
ListDataSources List all data sources for an API

Resolvers

Operation Description
CreateResolver Create a resolver
GetResolver Get a resolver by type and field
UpdateResolver Update a resolver
DeleteResolver Delete a resolver
ListResolvers List all resolvers for an API
ListResolversByType List resolvers for a specific type
ListResolversByFunction List resolvers attached to a specific function

Functions

Operation Description
CreateFunction Create a function configuration
GetFunction Get a function by ID
UpdateFunction Update a function
DeleteFunction Delete a function
ListFunctions List all functions for an API

Types

Operation Description
CreateType Create a type
GetType Get a type by name
UpdateType Update a type
DeleteType Delete a type
ListTypes List all types for an API

API Keys

Operation Description
CreateApiKey Create an API key
GetApiKey Get an API key by ID
UpdateApiKey Update an API key
DeleteApiKey Delete an API key
ListApiKeys List all API keys for an API

As on AWS, ApiKey.id is the key value itself (da2- followed by 26 lowercase alphanumerics) and is what clients send in the x-api-key header. There is no separate secret field.

Tags

Operation Description
TagResource Add tags to a resource
UntagResource Remove tags from a resource
ListTagsForResource List tags on a resource

Environment Variables

Operation Description
GetEnvironmentVariables Get environment variables for an API
PutEnvironmentVariables Set environment variables for an API

Domain Names

Operation Description
CreateDomainName Register a custom domain name
GetDomainName Get domain name configuration
UpdateDomainName Update domain name description
ListDomainNames List all domain names
DeleteDomainName Delete a custom domain name
AssociateApi Associate a domain name with a GraphQL API
GetAssociatedApi Get the API associated with a domain name
DisassociateApi Disassociate a domain name from a GraphQL API
ListApiAssociations List all associations for an API

Channel Namespaces

Operation Description
CreateChannelNamespace Create a channel namespace
GetChannelNamespace Get a channel namespace by name
UpdateChannelNamespace Update a channel namespace description
ListChannelNamespaces List all channel namespaces for an API
DeleteChannelNamespace Delete a channel namespace

Merged API Associations

Operation Description
CreateApiAssociation Associate a source API with a merged API
GetApiAssociation Get a merged API association
DeleteApiAssociation Delete a merged API association
ListApiAssociations List all merged API associations

Enhanced Metrics

Operation Description
GetEnhancedMetricsConfig Get the enhanced metrics configuration

Schema Registry

StartSchemaCreation validates the provided GraphQL SDL using graphql-java. Invalid schemas are rejected asynchronously (status FAILED with details after PROCESSING). Valid schemas are registered in an in-memory SchemaRegistry and persisted to the schema store.

On emulator startup, after storage load and orphan recovery, Floci rehydrates SUCCESS SDLs from the schema store into SchemaRegistry so POST /v1/apis/{apiId}/graphql works across restarts (memory/persistent/hybrid/wal).

The following AWS scalar types are pre-registered and available in any schema without requiring explicit scalar declarations:

Scalar Java Type Validation
AWSJSON String Valid JSON syntax
AWSDateTime String ISO 8601 datetime
AWSDate String ISO 8601 date (yyyy-MM-dd)
AWSTime String ISO 8601 time
AWSTimestamp Long Unix epoch seconds (0 to 32503680000)
AWSEmail String RFC 5322 email format
AWSURL String Valid URL
AWSPhone String E.164 format (+1234567890)
AWSIPAddress String IPv4 or IPv6
AWSBoolean Boolean Boolean value
AWSLong Long 64-bit signed integer
AWSInteger Integer 32-bit signed integer
AWSShort Integer 16-bit signed integer (-32768 to 32767)
AWSFloat Double IEEE 754 double-precision
AWSBigDecimal String Arbitrary-precision decimal
AWSBigInt String Arbitrary-precision integer
AWSByte String Base64-encoded byte array

The following AppSync directives are pre-defined and recognized in schemas:

Directive Locations Purpose
@aws_api_key OBJECT, FIELD_DEFINITION Require API key auth
@aws_iam OBJECT, FIELD_DEFINITION Require IAM auth
@aws_cognito_user_pools(cognito_groups: [String!]!) OBJECT, FIELD_DEFINITION Require Cognito user pool auth
@aws_oidc OBJECT, FIELD_DEFINITION Require OIDC auth
@aws_lambda OBJECT, FIELD_DEFINITION Require Lambda auth
@aws_subscribe(mutations: [String!]!) FIELD_DEFINITION Link subscription to mutation
@aws_auth(cognito_groups: [String!]!) OBJECT, FIELD_DEFINITION Require Cognito groups (ignored when additional auth modes exist)
@aws_delta_sync OBJECT Delta sync configuration

Unknown directives are rejected during schema registration.

Schema extensions (extend type Query { ... }) are supported natively through graphql-java.

GraphQL execute (data-plane)

Surface Path Content-Types
HTTP GraphQL POST /v1/apis/{apiId}/graphql application/json, application/graphql (+ charset)

Execute is a separate data-plane endpoint from the management API. Request body is GraphQL-over-HTTP JSON: { "query", "variables?", "operationName?" }.

Responses are application/json with AWS AppSync wire shapes (data / errors[] with top-level errorType / errorInfo). Most GraphQL syntax and validation errors return HTTP 200 with errors[].

Case HTTP Notes
Query / introspection / validation / syntax (incl. blank query) 200 Nullable fields may be null until DataFetchers (Phase 8)
HTTP subscription operation 200 OperationNotSupported (realtime WebSocket is a later phase)
Empty body / {} / [] / unparseable JSON / bad Content-Type 400 MalformedHttpRequestException
Missing operationName with multiple operations 400 BadRequestExceptionMissing operation name.
Unknown apiId 404 NotFoundException
API exists but no executable schema (incl. PROCESSING) 502 GraphQLSchemaExceptionNo schema definition exists. + x-amzn-errortype
Unexpected failure 500 InternalFailure
Missing/invalid/expired credentials, unconfigured mode, Lambda deny 401 UnauthorizedException — GraphQL does not run; x-amzn-errortype is set. Missing headers use message Missing authorization header.
Field directive mismatch, Cognito group miss, Lambda deniedFields, IAM field DENY 200 Field is null and errors[] contains UnauthorizedNot Authorized to access {field} on type {type} (no x-amzn-errortype)

Evidence for data-plane statuses (empty/[]/{} → 400; missing schema → 502): AppSync team sample in graphql/graphql-over-http#81 (@robzhu). The management API Reference lists GraphQLSchemaException as HTTP 400 for “schema not valid” on management operations — a different surface than the GraphQL execute data plane.

Execute authentication

Request auth runs after Content-Type and body parse and after API lookup (unknown apiId is still 404). It runs before schema lookup, so a missing schema with no credentials is 401, not 502.

Headers are classified by shape (not primary-then-fallback). If both x-api-key and SigV4 Authorization are present, SigV4 wins. AppSync does not fall back to the API key when IAM validation fails.

Missing required auth headers return HTTP 401 UnauthorizedException with message Missing authorization header. Invalid or expired credentials that are present still return 401 with You are not authorized to make this call.

Header shape Mode
Authorization starts with AWS4-HMAC-SHA256 AWS_IAM (wins over x-api-key if both are present)
x-api-key present API_KEY
Authorization: Bearer <jwt> Cognito and/or OIDC (matched by iss / aud or azp)
Other Authorization AWS_LAMBDA

Configured modes are the API default authenticationType plus additionalAuthenticationProviders. A classified mode that is not configured returns 401.

Mode Emulator notes
API_KEY Lookup by ApiKey.id, which is the key value (da2-…). Identity is absent (not {}). Default key expiry is 7 days when expires is omitted; stored expires is rounded down to the nearest hour. Create/UpdateApiKey require expires between 1 and 365 days from now (ApiKeyValidityOutOfBoundsException, 400). deletes is expires plus 60 days.
AWS_IAM Verifies a real header-signed SigV4 request (appsync service, fixed /v1/apis/{apiId}/graphql canonical path, 5-minute clock skew): the Credential= access key must resolve to a secret via IamService, and the signature must match. The legacy test/test pair is still emulator ALLOW, but it must be signed with secret test like any other key, and it is not a bypass. A temporary (ASIA...) credential must also present the X-Amz-Security-Token header matching the one issued for it. An unknown or unsigned key is always 401 and never becomes the account-root identity. Known keys additionally evaluate appsync:GraphQL.
Cognito / OIDC JWT signature is verified, not just decoded. Cognito checks the token against the issuing user pool's own RS256 signing key (alg, kid, issuer, audience/clientId, expiry); OIDC checks it against the configured issuer's published JWKS (via OIDC discovery), the same way the HTTP API JWT authorizer does. Both fail closed: an unreachable issuer, unsupported algorithm (including none), unmatched kid, or bad signature is 401. OIDC as the sole mode still skips the token's own iss claim check, but the signature is always verified against the configured issuer's keys. OIDC identity is {sub, issuer, claims} (no sourceIp).
Lambda AppSync isAuthorized contract via LambdaService.invoke (not an API Gateway policy document).

SDL field auth: unmarked fields require the API default mode. Additional modes unlock fields tagged @aws_api_key / @aws_iam / @aws_oidc / @aws_cognito_user_pools / @aws_lambda. Multiple directives on a field are OR. Field-level directives override type-level. @aws_auth is allowed on OBJECT \| FIELD_DEFINITION and is ignored when additional modes exist.

Duplicate API_KEY / AWS_IAM / AWS_LAMBDA (and the same Cognito pool or OIDC issuer) between default and additional providers is rejected on create/update with management 400 BadRequestException: Authentication type {TYPE} for additional authentication provider {N} already specified on the API. It can only be specified once. (N is 1-based in additionalAuthenticationProviders).

Pagination

All List operations support cursor-based pagination via query parameters:

Parameter Description
maxResults Maximum number of items to return
nextToken Opaque token for the next page

The nextToken is a Base64 URL-encoded integer offset. A missing token starts from offset 0. An invalid token returns InvalidNextTokenException (400).

# First page
aws appsync list-graphql-apis \
  --max-results 10 \
  --endpoint-url $AWS_ENDPOINT_URL

# Next page (use the nextToken from previous response)
aws appsync list-graphql-apis \
  --max-results 10 \
  --next-token "eyJvZmZzZXQiOjEwfQ==" \
  --endpoint-url $AWS_ENDPOINT_URL

Cascade Delete

Deleting a GraphQL API (DeleteGraphqlApi) automatically deletes all child resources:

  • Schema and schema creation status
  • All data sources
  • All resolvers
  • All functions
  • All types
  • All API keys
  • All channel namespaces
  • All domain name associations

This matches AWS behavior where deleting an API removes its entire configuration.

Not Implemented

These AWS AppSync capabilities are not yet implemented and are tracked in future phases:

  • DataFetcher / resolver dispatch (Phase 8): resolver mapping templates and field resolution with non-null values
  • Data source adapters (Phase 9): DynamoDB, Lambda, HTTP, EventBridge, OpenSearch, RDS connectors
  • Guardrails (Phase 10): query depth / complexity limits and related errors
  • Realtime subscriptions (Phase 11+): WebSocket real-time subscriptions
  • Caching: API-level and per-resolver caching
  • Merged API source management: AssociateMergedGraphqlApi, AssociateSourceGraphqlApi, StartSchemaMerge, ListTypesByAssociation
  • Data source introspection: StartDataSourceIntrospection, GetDataSourceIntrospection

Configuration

Variable Default Description
FLOCI_SERVICES_APPSYNC_ENABLED true Enable or disable the service
FLOCI_SERVICES_APPSYNC_VTL_MAX_LOOPS 10000 Maximum #foreach iterations a VTL resolver template may execute
FLOCI_SERVICES_APPSYNC_VTL_MAX_OUTPUT_CHARS 1048576 Maximum characters a VTL resolver template may render
FLOCI_SERVICES_APPSYNC_VTL_TIMEOUT_MILLIS 5000 Maximum wall-clock time a VTL resolver template may spend evaluating

Request/response mapping templates render inside the same VTL reflection sandbox described for API Gateway in api-gateway.md (SecureUberspector, with Class, ClassLoader, Runtime, ProcessBuilder, System, Thread, java.io.File and related classes/packages blocked), and are subject to the same three limits above. The loop cap truncates a #foreach at the configured iteration count and lets the template finish rendering with whatever output it produced up to that point; it does not fail the resolver. Exceeding the output-size or execution-time limit does fail the resolver, the same way any other VTL evaluation error does.

Examples

export AWS_ENDPOINT_URL=http://localhost:4566

# Create a GraphQL API
aws appsync create-graphql-api \
  --name my-api \
  --authentication-type API_KEY \
  --endpoint-url $AWS_ENDPOINT_URL

# Start schema creation
aws appsync start-schema-creation \
  --api-id API_ID \
  --definition 'type Query { hello: String }' \
  --endpoint-url $AWS_ENDPOINT_URL

# Create a data source (NONE type for local resolvers)
aws appsync create-data-source \
  --api-id API_ID \
  --name my-datasource \
  --type NONE \
  --endpoint-url $AWS_ENDPOINT_URL

# Create a resolver
aws appsync create-resolver \
  --api-id API_ID \
  --type-name Query \
  --field-name hello \
  --data-source-name my-datasource \
  --endpoint-url $AWS_ENDPOINT_URL

# Create an API key
aws appsync create-api-key \
  --api-id API_ID \
  --description "Test key" \
  --endpoint-url $AWS_ENDPOINT_URL

# List all APIs
aws appsync list-graphql-apis \
  --endpoint-url $AWS_ENDPOINT_URL

# Register a custom domain
aws appsync create-domain-name \
  --domain-name api.example.com \
  --certificate-arn arn:aws:acm:us-east-1:000000000000:certificate/123 \
  --endpoint-url $AWS_ENDPOINT_URL

# Associate domain with API
aws appsync associate-api \
  --domain-name api.example.com \
  --api-id API_ID \
  --endpoint-url $AWS_ENDPOINT_URL

# Execute a GraphQL query (data-plane; send credentials for the API auth mode)
curl -s -X POST "$AWS_ENDPOINT_URL/v1/apis/API_ID/graphql" \
  -H "Content-Type: application/json" \
  -H "x-api-key: da2-YOUR_API_KEY" \
  -d '{"query":"{ hello }"}'

# Create a channel namespace
aws appsync create-channel-namespace \
  --api-id API_ID \
  --name my-channels \
  --endpoint-url $AWS_ENDPOINT_URL