Skip to content

Migrate from VTL Templates to APPSYNC_JS #3378

@cheruvian

Description

@cheruvian

Describe the feature you'd like to request

Currently, Amplify API generates all resolvers using VTL (Velocity Template Language). This has a couple drawbacks:

  • Niche language: VTL has minimal adoption outside AppSync and legacy Java apps. Most developers have no prior exposure to it.
  • Custom AppSync extensions: AppSync adds custom VTL utilities ($util.dynamodb, $util.transform, etc.) not found in standard VTL documentation.
  • Poor debugging: VTL errors are cryptic, there's no step-through debugging, and the feedback loop is slow.
  • Difficult to extend: Customizing auto-generated resolvers requires learning VTL's quirks—inconsistent null handling, unusual syntax, lack of modern language features.
  • Weak tooling: Minimal IDE support, no autocomplete, no type checking.
  • Hard to test: Unit testing VTL in isolation is non-trivial compared to JavaScript.

AppSync now officially supports APPSYNC_JS as a first-class runtime. JavaScript is nearly universal among web/mobile developers, has excellent tooling, and resolvers can be tested with standard tools like Jest.

Describe the solution you'd like

Update the GraphQL transformer to generate APPSYNC_JS resolvers instead of VTL. This should be the new default for all generated resolvers including: pipeline resolvers, unit resolvers, and custom business logic.

Example of the improvement:

VTL (current):

#set($modelQueryExpression.expression = "#typeName = :typeName")
#set($modelQueryExpression.expressionNames = { "#typeName": "__typename" })
#set($modelQueryExpression.expressionValues = {
  ":typeName": $util.dynamodb.toDynamoDB($ctx.args.typeName)
})
#if(!$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == "DESC")
  #set($modelQueryExpression.scanIndexForward = false)
#else
  #set($modelQueryExpression.scanIndexForward = true)
#end

APPSYNC_JS (proposed):

import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const { typeName, sortDirection } = ctx.args;
  return {
    operation: 'Query',
    query: {
      expression: '#typeName = :typeName',
      expressionNames: { '#typeName': '__typename' },
      expressionValues: util.dynamodb.toMapValues({ ':typeName': typeName }),
    },
    scanIndexForward: sortDirection !== 'DESC',
  };
}

Describe alternatives you've considered

  1. Keep VTL as an option: Could offer both runtimes, but this adds maintenance burden and fragments the community/documentation. Better to move forward with JS.

  2. Manual rewrite: Eject resolvers and rewrite in JS manually, but this breaks the Amplify workflow and requires maintaining resolvers outside the Amplify lifecycle.

  3. Custom transformers: Build custom GraphQL transformers that output JS, but this duplicates significant work that Amplify should handle natively.

  4. Continue with VTL: Invest time mastering VTL, but this knowledge has limited transferability outside AppSync and the syntax remains error-prone.

Additional context

  • APPSYNC_JS is the modern choice: AWS added APPSYNC_JS as a first-class runtime specifically because VTL was a pain point. AWS Docs

    We now primarily support the APPSYNC_JS runtime and its documentation. Please consider using the APPSYNC_JS runtime and its guides here.

  • JavaScript is universal: Nearly every Amplify user already knows JS/TS. VTL requires learning a niche language with minimal transferable value.
  • TypeScript support: APPSYNC_JS supports TypeScript for type-safe resolver development with compile-time checks.
  • Testability: JS resolvers can be unit tested with standard tools using the @aws-appsync/utils package.
  • Linting available: @aws-appsync/eslint-plugin provides linting for JS resolvers.
  • Reduce maintenance burden: Maintaining one runtime (JS) is simpler than supporting both VTL and JS long-term.
  • Community alignment: VTL is consistently cited as a major friction point in Amplify API development. Migrating to JS removes a significant barrier to adoption and customization.

Is this something that you'd be interested in working on?

  • 👋 I may be able to implement this feature request

Would this feature include a breaking change?

  • ⚠️ This feature might incur a breaking change

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions