# How to use TypeScript with AppSync Lambda Resolvers

> ✏️ Edit - 2023-04-11: If you're interested in creating JavaScript resolvers with TypeScript rather than utilizing Lambda functions, check out [this alternative article](https://blog.graphbolt.dev/improving-developer-experience-with-typescript-how-to-write-strongly-typed-appsync-resolvers).

One of the great benefits of GraphQL is typing! Define your schema, and GraphQL enforces the input/output "shape" of your endpoints data.

If you are using Lambda as your AppSync resolvers with the *node.js* runtime, you might be using TypeScript, too. If you do, you might also be defining TS types that correspond to your schema. Doing this manually can be tedious, is prone to error, and is basically doing the same job twice! 🙁 Wouldn't it be great if you could import your GraphQL types into your code automatically?

In this article, I'll show you how to generate TypeScript types directly from your GraphQL schema, just by running a simple command line. Then, I'll teach you how to use those types in your Lambda resolvers.

Let's begin.

# Pre-requisites

You should already have a basic AppSync project setup with a defined GraphQL schema (If you don't have one already, you can use the example down below).

For the purpose of this tutorial, I will take this simple schema as an example:

```graphql
type Query {
	post(id: ID!): Post
}

type Mutation {
	createPost(post: PostInput!): Post!
}

type Post {
	id: ID!
	title: String!
	content: String!
	publishedAt: AWSDateTime
}

input PostInput {
	title: String!
	content: String!
}
```

# Setting up the project

## Install the dependencies

We will need to install three packages:

```bash
npm i @graphql-codegen/cli @graphql-codegen/typescript @types/aws-lambda  -D
```

The first two packages belong to the [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator) suite. The first one is the base CLI, while the second one is the plugin that generates TypeScript code from a GraphQL schema.

[@types/aws-lambda](https://www.npmjs.com/package/@types/aws-lambda) is a collection of TypeScript types for AWS Lambda. It includes all sorts of Lambda event type definitions (API gateway, S3, SNS, etc.), including one for AppSync resolvers (`AppSyncResolverHandler`). We'll use that last one later when we build our resolvers.

## Create the configuration file

It's time to configure `graphql-codegen` and tell it how to generate our TS types. For that, we'll create a `codegen.yml` file:

```yml
overwrite: true
schema:
  - schema.graphql #your schema file

generates:
  appsync.d.ts:
    plugins:
      - typescript
```

This tells *codegen* which schema file(s) it should use (in the example: `schema.graphql`), what plugin (`typescript`) and where the output should be placed (`appsync.d.ts`). Fell free to change these parameters to match your needs.

## Support for AWS Scalars

If you are using special [AWS AppSync Scalars](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html), you will also need to tell `graphql-codegen` how to handle them.

> 💡 You need to declare, at the minimum, the scalars that you use, but it might be a good idea to just declare them all and forget about it.

Let's create a new `appsync.graphql` file with the following content:

```graphql
scalar AWSDate
scalar AWSTime
scalar AWSDateTime
scalar AWSTimestamp
scalar AWSEmail
scalar AWSJSON
scalar AWSURL
scalar AWSPhone
scalar AWSIPAddress
```

> ⚠️ Don't place these types in the same file as your main schema. You only need them for code generation and they should not get into your deployment package to AWS AppSync.

We also need to tell codegen how to map these scalars to TypeScript. For that, we will modify the `codegen.yml` file. Add/edit the following sections:

```yml
schema:
  - schema.graphql
  - appsync.graphql # 👈 add this

# and this 👇
config:
  scalars:
    AWSJSON: string
    AWSDate: string
    AWSTime: string
    AWSDateTime: string
    AWSTimestamp: number
    AWSEmail: string
    AWSURL: string
    AWSPhone: string
    AWSIPAddress: string
```

# Generate the code

We are all set with the configuration. Time to generate some code! Run the following command:

```bash
graphql-codegen
```

> 💡 You can also add `"codegen": "graphql-codegen"` to you package.json under the "scripts" section, and use `npm run codegen`.

If you look in your working directory, you should now see an `appsync.d.ts` file that contains your generated types.

```ts
export type Maybe<T> = T | null;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string;
  String: string;
  Boolean: boolean;
  Int: number;
  Float: number;
  AWSDate: string;
  AWSTime: string;
  AWSDateTime: string;
  AWSTimestamp: number;
  AWSEmail: string;
  AWSJSON: string;
  AWSURL: string;
  AWSPhone: string;
  AWSIPAddress: string;
};

export type Query = {
  __typename?: 'Query';
  post?: Maybe<Post>;
};


export type QueryPostArgs = {
  id: Scalars['ID'];
};

export type Mutation = {
  __typename?: 'Mutation';
  createPost: Post;
};


export type MutationCreatePostArgs = {
  post: PostInput;
};

export type Post = {
  __typename?: 'Post';
  id: Scalars['ID'];
  title: Scalars['String'];
  content: Scalars['String'];
  publishedAt?: Maybe<Scalars['AWSDateTime']>;
};

export type PostInput = {
  title: Scalars['String'];
  content: Scalars['String'];
};
```

Notice that, apart from some helper types at the top, different types are being generated:

* `Scalars`
    

Contains all the basic scalars (ID, String, etc.) and the AWS custom Scalars.

* `Query` and `Mutation`
    

These two types describe the full Query and Mutation types.

* `Post`
    

This is our *Post* type from our schema translated into TypeScript. It is also the *return* value of the `post` query and the `createPost` mutation.

* `QueryPostArgs` and `MutationCreatePostArgs`
    

These types describe the input *arguments* of the `post` Query and the `createPost` mutation, respectively.

> 💡 Did you notice the name pattern here? Argument types are always named `Query[NameOfTheEndpoint]Args` and `Mutation[NameOfTheEndpoint]Args` in PascalCase. This is useful to know when you want to auto-complete types in your IDE.

# Use the generated types

Now that we have generated our types, it's time to use them!

Let's implement the `Query.post` resolver as an example.

Lambda handlers always receive 3 arguments:

* `event`: contains information about the input query (arguments, identity, etc)
    
* `context`: contains information about the executed Lambda function
    
* `callback`: a function you can call when your handler finishes (if you are not using async/promises)
    

The shape of an AppSync handler is almost always the same. It turns out that there is a [DefinitelyTyped package](https://www.npmjs.com/package/@types/aws-lambda) that already defines it. We installed it at the beginning of this tutorial. Let's use it!

The `AppSyncResolverHandler` type takes two arguments. The first one is the type for the `event.arguments` object, and the second one is the return value of the resolver.

In our case that will be: `QueryPostArgs` and `Post`, respectively.

Here is how to use it:

```ts
import db from './db';
import { AppSyncResolverHandler } from 'aws-lambda';
import {Post, QueryPostArgs} from './appsync';

export const handler: AppSyncResolverHandler<QueryPostArgs, Post> = async (event) => {
    const post = await db.getPost(event.arguments.id);

    if (post) {
        return post;
    }

    throw new Error('Not Found');
};
```

Now, our Lambda handler benefits from type-checking in 2 ways:

* `event.arguments` will be of type `QueryPostArgs` (with the benefits of auto-complete!)
    
* the *return* value, or the second argument of the `callback`, is expected to be of the same shape as `Post` (with an id, title, etc); or TypeScript will show you an error.
    

# Advanced usage

There are lots of options that let you customize your generated types. Check out [the documentation](https://graphql-code-generator.com/docs/plugins/typescript) for more details!

# Conclusion

By auto-generating types, you will not only improve your development speed and experience but will also ensure that your resolvers do what your API is expecting. You also ensure that your code types and your schema types are always in perfect sync, avoiding mismatches that could lead to bugs.

Don't forget to re-run the `graphql-codegen` command each time you edit your schema! It might be a good idea to automate the process or validate your types in your CI/CD pipeline.

%%[graphbolt]
