Improved developer experience of GraphQL APIs with JavaScript resolvers for AWS AppSync APIs
Learn how the developer experience of AppSync APIs has improved with the support of JavaScript resolvers.
AWS Admin
Amazon Employee
Published Oct 30, 2023
Last Modified Jun 21, 2024
AWS AppSync is a fully-managed serverless GraphQL API which enables serverless developers to build, deploy and also a powerful API layer for integrating seamlessly with other AWS services. Clearly, GraphQL is destined to be the future of APIs and learn about how you can get started with AppSync. In this blog, we will look into how AWS AppSync supporting JavaScript resolvers has improved the developer experience.
AppSync resolvers are the components responsible for integrating with different supported AppSync data sources such as - AWS Lambda functions, Amazon Aurora, Amazon DynamoDB, HTTP endpoints and many others. These resolvers have mapping templates for the GraphQL
type
, query
, mutation
and subscription
. Resolvers can be written in Velocity Template Language (VTL) or JavaScript runtime.Whenever integrating with AWS Services, resolvers (VTL or JavaScript based) would help with data manipulation for constructing the request mapping which the data source requires along with the response mapping where the response is constructed to the needed GraphQL schema. Additionally, resolvers support sharing of data between different resolver functions in a
pipeline resolver
with context
and arguments
. In both unit resolver
and pipeline resolver
, AppSync supports usage of different utility functions for data transformation of JSONs, Arrays Lists and generation of UUIDs.The above screenshot from AppSync console shows how a Pipeline resolver can have multiple functions which can integrate with the respective data source and sharing the data amongst the different resolver functions using
context.stash
.Velocity Template Language (VTL) is used to generate dynamic JSON content and based on Java environment.
For instance, the AppSync model
PrivateNote
.A mutation
createPrivateNote
for creating new notes, in a typical VTL resolver which sets the different default values and validates for authentication of the user with Amazon Cognito for identifying the owner of the note by using the util
functions on AppSync - $util.defaultIfNull()
, $$util.dynamodb.toDynamoDBJson()
and $$util.dynamodb.toMapValues()
to make the DynamoDB operation of PutItem
.VTL could be powerful but at certain scenarios, just isn't developer friendly for a developer who is building the Serverless GraphQL API.
In a complex
unit resolver
or pipeline resolver
, debugging is tedious as the VTL code is executed at runtime and having debugging breakpoints or ways to track the flow of execution is hard.It's possible to add debug logs using the util functions
$util.log.info()
and $util.log.error()
which are available in CloudWatch logs as the execution happens. However, this is not an efficient way of debugging during development.A good way to catch the errors is possible in the response mapping template.
When working with
if-else
statements or foreach
looping statement on VTL, the syntax of the VTL can be verbose.With the syntax and complex resolvers, often the direct data source to a database or HTTP can become hard to handle with the limited utility handler functions and in case of
pipeline resolver
, it can be a huge learning curve of how to use different functions, mapping request/response for all the functions and the parent resolver.For some use-cases and scenarios, using VTL resolvers may turn out to be overwhelming which results in developers moving towards Lambda function resolvers.
AppSync's JavaScript resolvers enable developers to use JavaScript runtime for all the resolvers instead of VTL.
Using the same example of
createPrivateNote
mutation, the resolver for it would be something like -This defines the
request()
and response()
methods for mapping with AppSync's mutation. With the familiarity of JavaScript, it's easy to modularize your resolvers with different JavaScript functions as you see in the example above, dynamodbPutRequest()
is invoked from the request()
method and whenever this returns, the response()
method would handle the response mapping with the defined schema.In JavaScript resolvers, you can import
@aws-appsync/utils
for all the util helper methods such as util.transform.toDynamoDBConditionExpression()
and util.dynamodb.toMapValues()
.Based on the stats and popularity of Node.JS usage for Serverless, developers would be familiar with JavaScript runtimes which are used in their existing workloads with Amazon Lambda functions or their Infrastructure as Code (IaC) where NodeJS and TypeScript have been widely adapted. And ensuring that their resolvers could now also be built with JavaScripts, this new enhancement is a celebration for Serverless developers.
Everyone loves to build with something that they are familiar with. As a developer who has used VTL and now played around with JavaScript resolvers, choosing JavaScript resolvers and building AppSync APIs is a faster and easier choice. Since developers have been used to JavaScript in their backend and also frontend, the learning curve to adapt VTL is reduced massively.
JavaScript resolvers also supports utility helper functions with
@aws-appsync/utils
the package.@aws-appsync/eslint-plugin
is the ESLint tool which detects issues in code during the development.- Using
util.transform
helper functions for easier transformations with DynamoDB and other data sources with different filters on Maps and Lists. - Built-in modules for different data sources makes it programming language compatible for developers to use functions and operations.
- Apart from the native JavaScript's flexibility to work with
datetime
, there areutil.time
helper functions available. - Working with different type classes -
Array
,String
,Object
from JavaScript.
Since JavaScript is supported, there are workarounds available for TypeScript to resolver more type strict.
The catch here is that, AppSync doesn't directly support TypeScript so it's possible to build AppSync APIs only from IDE given that you have the configurations set for building them as JavaScript code.
JavaScript resolvers support usage of custom and external libraries but there are some catch with JS runtime requirements on AppSync. While bundling with
esbuild
, it's possible to bundle with external libraries. Keep in mind that @aws-appsync/*
library is already available in JS runtime and this should not be bundled with the code.AppSync is deployed with AWS CDK and AWS Amplify which are developer friendly tools for building and deploying AppSync to the cloud, these already support JavaScript and TypeScript. Building AppSync resolvers will make the codebase more organized with the same tech stack. While developing, IDE support various extensions and tools for JavaScript making the code authoring process more smooth for development and debugging.
Choosing what's best for you is really important. JavaScript with no doubt has the better experience while building GraphQL APIs on AppSync when compared with VTL. Some of the patterns that I've seen is that for simpler resolvers such as using Lambda function as the data source, still makes sense to use VTL as in that case VTL is only forwarding all the
context
arguments to Lambda function.Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.