Mark Pollmann's blog

Refactor CDK AppSync resolvers from Velocity to JavaScript

· Mark Pollmann

If you started using AppSync to create a GraphQL API in the past you might have resolvers written/generated in this beautiful templating language called Velocity. At the time it was the only supported way to write resolvers.
In 2022 AWS started to support JavaScript resolvers which for most people is more natural to write and better understood. Here I want to show how you can convert a TypeScript CDK project.

In your Stack definition

For functions you need to remove the properties requestMappingTemplate and responseMappingTemplate

//in stack.ts. Before
const myFunction = new AppsyncFunction(this, myFunctionName, {
      api,
      dataSource: myDataSource,
      name: myFunctionName,
      // remove
      requestMappingTemplate: MappingTemplate.fromFile(
        "./cdk/graphql/mappings/functions/SomeType.request.vtl",
      ),
      // remove
      responseMappingTemplate: MappingTemplate.fromFile(
        "./cdk/graphql/mappings/functions/SomeType.response.vtl",
      ),
    });

and replace them with two new properties runtime and code.

//in stack.ts. After
const myFunction = new AppsyncFunction(this, myFunctionName, {
      api,
      dataSource: setPhotoDataSource,
      name: myFunctionName,
      // add
      runtime: aws_appsync.FunctionRuntime.JS_1_0_0,
      // add
      code: aws_appsync.Code.fromAsset(
        "./cdk/graphql/mappings/functions/SomeType.js",
      ),
    });

The new function combines the two mapping files and exports two functions:
request(context) which contains the same logic as requestMappingTemplate and response(context) with contains the responseMappingTemplate logic.

//./cdk/graphql/mappings/functions/SomeType.js

export function request(context) {
   return something;
}

export function response(context) {
  return something;
}

For resolvers pretty much the same process applies:

api.createResolver("MyAwesomeResolver", {
      fieldName: "someField",
      pipelineConfig: [
        myFunction
      ],
      // remove
      requestMappingTemplate: MappingTemplate.fromFile(
        "./cdk/graphql/mappings/Query.someField.request.vtl",
      ),
      // remove
      responseMappingTemplate: MappingTemplate.fromFile(
        "./cdk/graphql/mappings/Query.someField.response.vtl",
      ),
      typeName: "Query",
    });

Becomes:

 api.createResolver("MyAwesomeResolver", {
      fieldName: "someField",
      pipelineConfig: [
        myFunction
      ],
      // add
      runtime: aws_appsync.FunctionRuntime.JS_1_0_0,
      // add
      code: aws_appsync.Code.fromAsset(
        "./cdk/graphql/mappings/Query.someField.js",
      ),
      typeName: "Query",
    });

Writing your new resolver function

Like described above, write your request mapping code inside the request function and your response mapping code inside the response function. Most of it should be straightforward, read the documentation of Velocity’s utility functions if needed.

Gotchas and bug squashing

If AppSync deems your new code not correct you get an ugly error during cdk deploy: “One or more errors found… Rollback”.
These are the most common errors I encountered:

Using Velocity’s syntax in JavaScript

In Velocity you add properties of objects with

$myObject.put("someKey", $someVariable)

and retrieve them with

$myObject.get("someKey)

Don’t blindly copy the syntax in your new code, use valid Javascript:

// assign
myObject.someKey = someVariable;

// retrieve
myObject.someKey;

Throwing erros

This is not allowed in javascript resolvers, even though it is valid JavaScript:

throw new Error('something went wrong');

Instead use the util function (don’t forget to npm install it)

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

util.error('something went wrong');

To stringify or not to stringify

Be sure you actually need to JSON.stringify properties when you saw a $util.toJson in the corresponding Velocity code. You probably don’t.