Refactor CDK AppSync resolvers from Velocity to JavaScript
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.
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 resolver functions
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.