Skip to content

AppSync JS Development

The AWS AppSync JavaScript runtime (APPSYNC_JS) is a highly restricted environment. While it targets ES6 compatibility, the CloudFormation validator and the runtime engine have several "silent" constraints that differ from standard Node.js.


🚫 The "util.toJson" Trap

One of the most significant "gotchas" is the inconsistency between old VTL documentation and the new JS runtime.

Helper Status in APPSYNC_JS Native Alternative
util.toJson() Invalid (Runtime Error) JSON.stringify()
util.parseJson() Unreliable (Validator Risk) JSON.parse()

Rule: Always prefer native JavaScript globals (JSON, Math) over util wrappers for standard object/string manipulation.


🔄 Iteration & Closures

The AppSync validator (CloudFormation side) often fails to trace variable types across function boundaries (closures).

❌ Dangerous: .forEach()

Passing a function as an argument to another function is often rejected by the runtime compiler.

// This might pass Vitest but will fail deployment
items.forEach(item => {
    ...
});

✅ Safe: for...of or for(;;)

Use standard loops to keep everything in a single execution scope.

for (const item of items) {
    // Linear execution, safe for validator
}

📦 Scoping & Declarations

1. No Top-Level Code

Every line of code (variables, constants, helpers) must be inside the exported request or response functions. Top-level definitions will trigger a "one or more errors" message.

2. No var

AppSync JS runs as an ES Module. Use let or const exclusively. var can trigger legacy scoping issues that confuse the static analyzer.

3. Explicit Identity fallbacks

Don't rely on deep optional chaining for identity. Use explicit checks to satisfy the validator.

let userId = (ctx.identity && ctx.identity.sub) ? ctx.identity.sub : "unknown";

🛡️ Validation Tooling

Since local Vitest runs in Node.js (which is more permissive), passing local tests does not guarantee a successful deployment.

Always run the AppSync Evaluator before deploying:

task test:eval

This command uses aws appsync evaluate-code to hit the actual AWS runtime compiler and catch INVALID_FUNCTION_INVOCATION or PARSE_ERROR issues without waiting for a full CloudFormation stack update.


📑 Type Safety (AWSJSON)

If your GraphQL schema defines a field as AWSJSON, the resolver must return a stringified JSON value, not a JS Object.

// Schema: extendedNutrients: AWSJSON
return {
    ...item,
    extendedNutrients: JSON.stringify(item.nutrients) // Must be string
};