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.
✅ Safe: for...of or for(;;)
Use standard loops to keep everything in a single execution scope.
📦 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.
🛡️ 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:
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.