Front-End Web & Mobile

AWS AppSync now supports JavaScript for all resolvers in GraphQL APIs

AWS AppSync is a managed service that makes it easy to build scalable APIs that connect applications to data. Whether you’re looking to power your web applications with an API that connects to Amazon DynamoDB, or you are looking to build a real-time dashboard that provides live event updates to your users, AppSync can help you get started and seamlessly scale to meet your demand. Last year, AppSync introduced support for JavaScript in AppSync pipeline resolvers and AppSync functions. This allowed customers to write their resolvers in JavaScript and execute them on the AppSync JavaScript runtime to resolve their queries. JavaScript resolvers allow customers to do more in their resolvers without reaching for additional compute resources for common transformation and translation operations.

Today AppSync is extending support for JavaScript to unit resolvers. Developers can now handle single data source access patterns in JavaScript with a single resolver. Developers can continue to handle complex access patterns and mix Velocity Template Language (VTL) functions along with JavaScript functions in a pipeline resolver. In this blog post, we will go over how you can use JavaScript resolvers in your own API.

Getting started

You can get started with JavaScript resolvers in the AWS AppSync console. In the console, select “Create API” and select “design from scratch”. Name your API, and on step 2, select “Create type backed by a DynamoDB table now”. Here the console can assist you in creating a new GraphQL type and its operations. It will create a DynamoDB table to back the type. Let’s create a Todo type with the followings fields: id, owner, name, severity, and dueOn. The console pre-selects the AppSync JavaScript runtime for the resolvers.

Configure a data model backed by a DynamoDB table

Next, configure the table to store your data. Name the table “todos” and set the primary key to id, and the sort key to owner. Add an index to fetch your todos by owner name. Name the index “owner-dueon-index”, set the primary key to owner and set the sort key to dueOn.

Configure model table

Click “next”, confirm your options and create your API. Your API is created and your resolvers are automatically generated. From the schema page, you can select one of your generated resolvers, or attach a new resolver to any field. Let’s modify the schema so that an id does not have to be provided to create a Todo. Update CreateTodoInput to this:

input CreateTodoInput {
    id: String
    owner: String!
    name: String
    severity: Int
    dueOn: AWSDate
}

Next, in the “Resolvers” pane, find the createTodo field and click on its resolver. You only need to edit one line of code. Modify this line:

const { id, owner, ...values } = ctx.args.input;

to:

const { id = util.autoId(), owner, ...values } = ctx.args.input;

This simple change uses the AppSync utility autoId to generate a new unique id if one is not provided in the mutation. From the “Queries” screen, you can interact with the API be sending queries and mutations, or setting up subscriptions. For example, to create a todo:

mutation create {
  createTodo(input: {name: "first todo", owner: "brice", severity: 10, dueOn: "2023-08-10"}) {
    dueOn
    id
    name
    owner
    severity
  }
}

You can then fetch it by using the owner name:

query byOwner {
  queryTodosByOwnerDueonIndex(owner: "brice") {
    items {
      id
      name
      owner
    }
  }
}

You can attach a resolver to any schema field. To attach a resolver, select the resolver type, the runtime, and the data source.

Create JavaScript resolver

Once the resolver is created, you can select from several code samples to start writing your own resolvers.

Using the AWS CDK

You can use JavaScript resolvers in your AWS CDK projects. When working locally, you can take advantage of TypeScript, AppSync utility libraries and types, and AWS Amplify codegen to enhance your coding experience. To get started with TypeScript in a CDK project, you can use the Todo API CDK example in the AppSync resolver samples repository. This example uses a local helper construct that automatically links resolvers to data sources based on the file name. Start by cloning the repository locally and install the dependencies:

$ git clone https://github.com/aws-samples/aws-appsync-resolver-samples.git
$ cd aws-appsync-resolver-samples
$ cd examples/cdk/constructs/appsync-helper
$ npm run init
$ cd ../../dynamodb-todo-app 
$ npm install

The resolvers for the app are located in lib/appsync/resolvers. Take a look at Mutation.createTodo.[todos].ts. It uses types from auto-generated code at ./lib/appsync/codegen. The codegen types are automatically generated from the schema file located in ./lib/appsync/schema.graphql. To generate the codegen, run:

$ npm run codegen

(which calls amplify codegen for you)

You can now use the types in your code. You can rerun the codegen any time your schema changes to update your Type. This is the resolver for Mutation.createTodo

import { util, Context } from '@aws-appsync/utils';
import { CreateTodoMutationVariables } from '../codegen';
import { dynamodbPutRequest } from './utils';
export { checkErrorsAndRespond as response } from './utils';

export function request(ctx: Context<CreateTodoMutationVariables>) {
    const { input: values } = ctx.arguments;
    const key = { id: util.autoId() };
    const condition = { id: { attributeExists: false } };
    console.log('--> create todo with requested values: ', values);
    return dynamodbPutRequest({ key, values, condition });
}

This resolver uses a helper function dynamodbPutRequest to create the DynamoDB PutItem request. It also uses a helper function checkErrorsAndRespond that it exports as response to handle the response handling. Since this is a common pattern, instead of writing it in every resolver, we add it to the list of project utilities.

/**
 * Checks for errors and returns the `result
 */
export function checkErrorsAndRespond(ctx: Context) {
    if (ctx.error) {
        util.error(ctx.error.message, ctx.error.type);
    }
    return ctx.result;
}

When working locally with resolvers, you can use TypeScript, imports, exports, and external utilities. You just bundle your code before saving your resolver. To learn more about bundling see the AppSync documentation. This project automatically bundles and attaches your resolvers to data sources for you. To learn more, see the helper construct.

When you are ready to deploy your changes, you can run:

$ npm run cdk deploy

When you are done with your project, you can delete your resources by running:

$ npm run cdk destroy

You can find additional examples in the repository as well as resolver code samples.

Conclusion

In this article, we went over the new support for JavaScript in unit resolvers. We covered how to easily get started from the console, and we looked at how you can use features such as TypeScript in your unit resolvers in a CDK project. You can now take advantage of the AppSync JavaScript runtime in all your resolvers and functions, which will make it easier to handle all of your data access use cases in a familiar way. If you have not tried JavaScript resolvers, now is a good time to do so. Remember that you can easily get started with samples in the AWS AppSync console and with the samples repo.

To learn more about JavaScript resolvers, see the documentation and the tutorials. For an in depth guided walkthrough, please visit the AWS AppSync immersion day workshop. You can find easy to use samples and guides in the samples repository.