AWS News Blog

AWS AppSync GraphQL APIs Supports JavaScript Resolvers

Voiced by Polly

Starting today, AWS AppSync supports JavaScript resolvers and provides a resolver evaluation engine to test them before publishing them to the cloud.

AWS AppSync, launched in 2017, is a service that allows you to build, manage, and host GraphQL APIs in the cloud. AWS AppSync connects your GraphQL schema to different data sources using resolvers. Resolvers are how AWS AppSync translates GraphQL requests and fetches information from the different data sources.

Until today, many customers had to write their resolvers using Apache Velocity Template Language (VTL). To write VTL resolvers, many developers needed to learn a new language, and that discouraged them from taking advantage of the capabilities that resolvers offer. And when they did write them, developers faced the challenge of how to test the VTL resolvers. That is why many customers resorted to writing their complex resolvers as AWS Lambda functions and then creating a simple VTL resolver that invoked that function. This adds more complexity to their applications, as now they have to maintain and operate this new Lambda function.

AWS AppSync executes resolvers on a GraphQL field. Sometimes, applications require executing multiple operations to resolve a single GraphQL field. When using AWS AppSync, developers can create pipeline resolvers to compose operations (called functions) and execute them in sequence. Each function performs an operation over a data source, for example, fetching an item from an Amazon DynamoDB table.

How a function works

Introducing AWS AppSync JavaScript pipeline resolvers
Now, in addition to VTL, developers can use JavaScript to write their functions. You can mix functions written in JavaScript and VTL inside a pipeline resolver.

This new launch comes with two new NPM libraries to simplify development: @aws-appsync/eslint-plugin to catch and fix problems quickly during development and @aws-appsync/utils to provide type validation and autocompletion in code editors.

Developers can test their JavaScript code using AWS AppSync’s new API command, evaluate-code. During a test, the code is validated for correctness and evaluated with mock data. This helps developers validate their code before pushing their changes to the cloud.

With this launch, AWS AppSync becomes one of the easiest ways for your applications to talk to almost any AWS service. You can write an HTTP function that calls any AWS service with an API endpoint using JavaScript and use that function as part of your pipeline. For example, you can create a pipeline resolver that is invoked when a query on a GraphQL field occurs. This field returns the translated text in Spanish of an item stored in a table. This pipeline resolver is composed of two functions, one that fetches data from a DynamoDB table and one that uses Amazon Translate API to translate the item text into Spanish.

function awsTranslateRequest(Text, SourceLanguageCode, SourceLanguageCode) {
  return {
    method: 'POST',
    resourcePath: '/',
    params: {
      headers: {
        'content-type': 'application/x-amz-json-1.1',
        'x-amz-target': 'AWSShineFrontendService_20170701.TranslateText',
      },
      body: JSON.stringify({ Text, SourceLanguageCode, SourceLanguageCode }),
    },
  };
}

Getting started
You can create JavaScript functions from the AWS AppSync console or using the AWS Command Line Interface (CLI). Let’s create a pipeline resolver that gets an item from an existing DynamoDB table using the AWS CLI. This resolver only has one function.

When creating a new AWS AppSync function, you need to provide the code for that function. Create a new JavaScript file and copy the following code snippet.

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

/**
 * Request a single item from the attached DynamoDB table
 * @param ctx the request context
 */
export function request(ctx) {
  return {
    operation: 'GetItem',
    key: util.dynamodb.toMapValues({ id: ctx.args.id }),
  };
}

/**
 * Returns the DynamoDB result directly
 * @param ctx the request context
 */
export function response(ctx) {
  return ctx.result;
}

All functions need to have a request and response method, and in each of these methods, you can perform the operations for fulfilling the business need.

To get started, first make sure that you have the latest version of the AWS CLI, that you have a DynamoDB table created, and that you have an AWS AppSync API. Then you can create the function in AWS AppSync using the AWS CLI create-function command and the file you just created. This command returns the function ID. To create the resolver, pass the function ID, the GraphQL operation, and the field where you want to apply the resolver. In the documentation, you can find a detailed tutorial on how to create pipeline resolvers.

Testing a resolver
To test a function, use the evaluate-code command from AWS CLI or AWS SDK. This command calls the AWS AppSync service and evaluates the code with the provided context. To automate the test, you can use any JavaScript testing and assertion library. For example, the following code snippet uses Jest to validate the returned results programmatically.

import * as AWS from 'aws-sdk'
import { readFile } from 'fs/promises'
const appsync = new AWS.AppSync({ region: 'us-east-2' })
const file = './functions/updateItem.js'

test('validate an update request', async () => {
  const context = JSON.stringify({
    arguments: {
      input: { id: '<my-id>', title: 'change!', description: null },
    },
  })
  const code = await readFile(file, { encoding: 'utf8' })
  const runtime = { name: 'APPSYNC_JS', runtimeVersion: '1.0.0' }
  const params = { context, code, runtime, function: 'request' }

  const response = await appsync.evaluateCode(params).promise()
  expect(response.error).toBeUndefined()
  expect(response.evaluationResult).toBeDefined()
  const result = JSON.parse(response.evaluationResult)
  expect(result.key.id.S).toEqual(context.arguments.input.id)
  expect(result.update.expressionNames).not.toHaveProperty('#id')
  expect(result.update.expressionNames).toHaveProperty('#title')
  expect(result.update.expressionNames).toHaveProperty('#description')
  expect(result.update.expressionValues).not.toHaveProperty(':description')
})

In this way, you can add your API tests to your build process and validate that you coded the resolvers correctly before you push the changes to the cloud.

Get started today
The support for JavaScript AWS AppSync resolvers in AWS AppSync is available for all Regions that currently support AWS AppSync. You can start using this feature today from the AWS Management Console, AWS CLI, or Amazon CloudFormation.

Learn more about this launch by visiting the AWS AppSync service page.

Marcia

Marcia Villalba

Marcia Villalba

Marcia Villalba is a Principal Developer Advocate for Amazon Web Services. She has almost 20 years of experience working in the software industry building and scaling applications. Her passion is designing systems that can take full advantage of the cloud and embrace the DevOps culture.