Skip to content

AppSync GraphQL

AppSync GraphQL routes resolver events and batches to Go callbacks. Import github.com/rambow-cloud/powertools-lambda-go/eventhandler/appsyncgraphql. Register queries, mutations and other type/field pairs before serving invocations.

See installation and the compatibility baseline.

Complete example

Build this complete Lambda example at ./examples/appsyncgraphql with CGO_ENABLED=0. Configure AppSync to send its resolver event to the Lambda data source; the example registers Query.hello.

package main

import (
    "context"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/rambow-cloud/powertools-lambda-go/eventhandler/appsyncgraphql"
)

func main() {
    app := appsyncgraphql.New(appsyncgraphql.Options{})
    app.OnQuery("hello", func(_ context.Context, arguments, event any) (any, error) {
        return map[string]any{"message": "Hello", "arguments": arguments}, nil
    })
    lambda.Start(app.Resolve)
}

Input and output

The full AppSync event below returns {"arguments":{"name":"Ada"},"message":"Hello"}. This is the resolver value, not an HTTP proxy response or a log line. The callback receives both the arguments object and complete event. A missing route raises ResolverNotFoundException. Successful resolution does not automatically write an application log. Sending only arguments and the route names is not a valid resolver envelope.

{
  "arguments": {"name": "Ada"},
  "identity": null,
  "source": null,
  "prev": null,
  "stash": {},
  "request": {"headers": {}, "domainName": null},
  "info": {"parentTypeName": "Query", "fieldName": "hello", "variables": {}}
}

Objects and lifecycle

Object Responsibility
app Reusable route/exception registry; no service client.
Callback arguments arguments is the field argument value; event retains identity, source and resolver context.
Router Split registrations into modules and include snapshots into the resolver.

TypeScript feature coverage

Compared with the official v2.35.0 appsync-graphql guide and the pinned npm implementation. The table maps capabilities; it does not certify every native type or service behavior.

TypeScript feature Go API or approach Compatibility scope
Resolver / nested mappings OnQuery, OnMutation, OnResolver Explicit type/field keys replace decorators and scope binding.
Split routers NewRouter, IncludeRouter Snapshots included routes.
Batch resolution OnBatchResolver, batch options Aggregated or sequential individual processing with explicit error policy.
Exception handling OnException, named errors Errors remain distinct from resolver-not-found/invalid-batch exceptions.
Scalars AWSDate, AWSTime, AWSDateTime, AWSTimestamp, MakeID Explicit Go clock values; native Date boundaries differ.
Lambda context / logging Callback ctx, Options.Diagnostic Optional Logger; no implicit structured business log.

Executable evidence: eventhandler/appsyncgraphql/reference_test.go. See the verification scope and project progress for open gates.

Public mapping

TypeScript export or member Go mapping
AppSyncGraphQLResolver Resolver, New, Resolve
Router Router, NewRouter, promoted registration methods
resolver, onQuery, onMutation OnResolver, OnQuery, OnMutation
batchResolver, onBatchQuery, onBatchMutation OnBatchResolver, OnBatchQuery, OnBatchMutation
exceptionHandler OnException, exact error names and an ExceptionHandler
includeRouter IncludeRouter, ordered variadic routers
ResolverNotFoundException Same concrete exported type and Runtime API error name
InvalidBatchResponseException Same concrete exported type and Runtime API error name
awsDate, awsTime, awsDateTime AWSDate, AWSTime, AWSDateTime with explicit time.Time and optional offset
awsTimestamp AWSTimestamp(time.Time)
makeId MakeID, random UUID v4
Decorators and scope binding Go closures and bound method values

OnResolver and OnBatchResolver take an explicit type name. Convenience methods provide Query and Mutation defaults. Route keys retain the reference's literal typeName + "." + fieldName concatenation. Duplicate registrations replace the previous handler and emit a warning; single and batch routes remain separate.

Batch and error behavior

Batch handlers aggregate by default. Both handler arguments contain the entire event slice. Return a non-nil slice or array; response length is not forced to match input length. Native top-level slices are materialized as JSON arrays, including byte slices. Nil slices are rejected because they serialize as null.

BatchOptions{Individual: true} selects sequential execution. Only the first event selects a route, even when later fields or type names differ. Each handler receives that item's arguments and complete event. Failures log diagnostics and append null, then processing continues. Add ThrowOnError: true to stop on the first failure and send it through the resolver's outer exception handling.

Returned Go errors and error-valued panics participate in exception handling. ErrorName() string supplies a custom name; ordinary errors use Error. NamedError is a convenient implementation. Non-error panic values produce {"error":"An unknown error occurred"} without invoking an exception handler. Exception handlers match exact names. If an exception handler fails, diagnostics record that failure and the response formats the original error. Missing-resolver and invalid-batch exceptions always propagate; handlers cannot override them. Go error wrapping is recognized through errors.As.

Invalid event shapes warn and return nil. The shape guard is intentionally lighter than Parser's AppSync schema. An empty batch reproduces the reference's propagated TypeError using a concrete Go type with the same Lambda Runtime API error name; it does not silently succeed. Top-level JavaScript undefined maps to Go nil. Go cannot reproduce arbitrary JavaScript prototype, Promise, decorator or mutable scope behavior. Typed AWS SDK structs need explicit conversion to maps; the resolver does not silently serialize arbitrary native objects.

Options.Diagnostic receives the invocation context for resolution and background context for registration. It receives debug calls regardless of environment. The default sink writes errors/warnings to stderr; debug output goes to stdout only when Commons-trimmed AWS_LAMBDA_LOG_LEVEL equals DEBUG. An individual batch failure uses an empty message with its error, matching the reference's error-only diagnostic. Concurrent invocations may call the sink concurrently.

Scalars and evidence

Scalar helpers accept an explicit clock value for reproducible output. Offsets use hours, including fractions, in the inclusive range -12 to +14. Nonzero offset suffixes retain the reference's seconds component, for example +05:30:00. Date-only output also includes its suffix. Formatting preserves milliseconds, un-padded years, JavaScript Date clipping and NaN offset behavior in the tested range. Timestamp seconds floor negative instants. Go nanosecond inputs, instants outside the JavaScript Date domain and arbitrary native serialization remain explicit compatibility audit items.

tools/reference/generate-appsync-graphql.mjs executes actual pinned public exports: 114 resolver scenarios and 91 scalar cases. Go checks ordered calls, diagnostics, values and errors, plus 64 concurrent contexts, sequential batches, mutation ownership, callback reentrancy, panic/error identity and UUID structure. These checks do not establish live AppSync service or complete language parity. Remaining acceptance requirements are tracked in APPSYNC_GRAPHQL_PLAN.md.