Simplify AWS Appsync GraphQL API creation with strongly typed Typescript resolvers

sudokar

sudokar

Posted on July 30, 2023

Simplify AWS Appsync GraphQL API creation with strongly typed Typescript resolvers

This post assumes familiarity with AWS Appsync and AWS CDK in general

Introduction

In this post, we'll look into creating an AWS Appsync GraphQL API with TypeScript using cdk-appsync-typescript-resolver AWS CDK construct. This powerful construct simplifies the creation of AWS AppSync resolvers in your CDK project, making it easier and more efficient for developers to work with AppSync.

Complete code is available in Github for reference

Codegen

We can generate Typescript types using GraphQL Code Gen easily.

These types are generated using Codegen CLI command graphql-codegen for this graphql schema using this codegen config file.

Note: Alternatively, We can use Amplify codegen as well

Resolver in Typescript

We can start writing our resolvers in Typescript using the Codegen generated types and the Appsync's utils package

Lets look at an example resolver. Link to File

import { Context, DynamoDBPutItemRequest, util } from "@aws-appsync/utils";
import { MutationAddTodoArgs, Todo } from "../types/appsync";

export function request(
  ctx: Context<MutationAddTodoArgs>
): DynamoDBPutItemRequest {
  const { id, ...attrValues } = ctx.args;

  return {
    operation: "PutItem",
    key: {
      id: util.dynamodb.toDynamoDB(id),
    },
    attributeValues: util.dynamodb.toMapValues({
      ...attrValues,
    }),
  };
}

export function response(
  ctx: Context<MutationAddTodoArgs, object, object, object, Todo>
) {
  return ctx.result;
}
Enter fullscreen mode Exit fullscreen mode

This looks a lot better than writing resolvers in Javascript or VTL 😓

We can use types for context, various data sources supported by Appsync and built-in utilities along with generated types for GraphQL schema, which enhances the reliability and maintainability code through static type checking

Transpiling and Bundling

Unfortunately, we can't just sent the resolvers written in Typescript to Appsync yet. Like we transpile Typescirpt to Javascript for AWS Lambda, We will have to do the same for Appsync.

Unfortunately, as of today there is no built-in capability in CDK to do this for us. We will have to do it ourself using Esbuild or something similar, which transpiles and bundles code from Typescript to Javascript.

Using Esbuild's Javascript API, We have a custom CDK construct cdk-appsync-typescript-resolver that will automate the transpilation and bundling process for us.

An example of creating Typescript function using the custom construct AppsyncTypescriptFunction. This abstracts the transpiling and bundling process and setting default properties.

const addTodo = new AppsyncTypescriptFunction(this, "AddTodoFunction", {
      name: "addTodo",
      api: graphqlApi,
      dataSource: dynamoDataSource,
      path: path.join(__dirname, "addTodo.ts"),
      replaceStrings: {
          ENV: "prod",
      },
    });
Enter fullscreen mode Exit fullscreen mode

JS resolvers doesn't support unit resolvers, supports only pipeline resolvers. When we want to create a unit resolver, we will have to use bit of boilerplate code and settings to make the pipeline resolver as unit resolver.

An added functionality to the construct is to define a property replaceStrings, which takes a map of from and to strings, used to replace strings in the transpiled and bundled code.

We have another CDK construct TSExpressPipelineResolver to use the AppsyncTypescriptFunction and sets up required boilerplate for us.

An example of using TSExpressPipelineResolver construct.

new TSExpressPipelineResolver(this, "AddTodoResolver", {
      api: graphqlApi,
      typeName: "Mutation",
      fieldName: "addTodo",
      tsFunction: addTodo,
    });
Enter fullscreen mode Exit fullscreen mode

Note: We can use instance of AppsyncTypescriptFunction construct, which extends appsync.AppsyncFunction, in a regular appsync.Resolver construct too

Deploying the stack

You can find the complete code of this demo on GitHub.

cdk deploy
Enter fullscreen mode Exit fullscreen mode

Tip

Catch invalid syntax (things not supported by Appsync's JS Resolver) while writing resolvers using the Eslint plugin @aws-appsync/eslint-plugin

Conclusion

By using the constructs from cdk-appsync-typescript-resolver, we can now take advantage of static typing and code editor's IntelliSense & inlay hints to make Appsync's resolvers easy and efficient. Generation of types from GraphQL schema makes it even more efficient. And also, With @aws-appsync/eslint-plugin we can get the benefit of finding invalid syntax while writing code.

Hope this was helpful!

💖 💪 🙅 🚩
sudokar
sudokar

Posted on July 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related