AWS IAM Auth: Calling AppSync Mutations from Lambda (Step-by-Step-Guide)
Tags: appsync, graphql, serverless, awsappsync
Published: 2023-12-03 15:23:25

Have you recently started working in AppSync and wondering how to trigger mutations or queries from Lambda functions? While you can use Axios to complete this, to make the call more secure, we will approach using IAM to call mutations from Lambda which will add an extra layer of protection. This blog aims to make dev life easier with the approach on how to implement the below scenario.

## Scenario
In This blog i'll introduce the mutation schema which we will be using to call inside the Lambda, and then will be using IAM auth to get token so that lambda is allowed to call the mutation. now lets explore the approach

## pre-requirities:

1. Create the required AppSync mutation.
2. Set up a Lambda function that will make the mutation call.
3. Ensure necessary permissions are in place to trigger the mutation from Lambda.

## Soultion
 **Step1:** Let's create an AppSync mutation which will be use to create user in DDB. Below is the schema for the createUser mutation:

```
      mutation MyMutation(
        $age: Int!, 
        $id: String!, 
        $isDeleted: String!, 
        $name: String!
      ) {
        createUser(
            age: $age, 
            id: $id, 
            isDeleted: $isDeleted, 
            name: $name
        ) {
          body {
            age
            id
            isDeleted
            name
          }
          message
          status
        }
      }
```
**Step2:** As the mutation is been created and we require IAM permission for your lambda, with the policy to trigger the appsync mutation, for that let's specify the region, accountId, and apiId in the resource.

```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"appsync:GraphQL"
			],
			"Resource": [
				"arn:aws:appsync:region:accountId:apis/apiId/types/Mutation/fields/createUser"
			]
		}
	]
}
```
**Step3:** once all the necessary setup are done, you need to go to your lambda and paste the below code which will call the mutation, and install the required libraries which will be used to call the mutation:

```
'axios', '@aws-crypto/sha256-js', '@aws-sdk/credential-provider-node',  '@aws-sdk/signature-v4', '@aws-sdk/protocol-http'
```
Now, copy the below Lambda code:

```
const axios = require('axios');
const crypto = require('@aws-crypto/sha256-js');
const { defaultProvider } = require('@aws-sdk/credential-provider-node');
const { SignatureV4 } = require('@aws-sdk/signature-v4');
const { HttpRequest } = require('@aws-sdk/protocol-http');
const { Sha256 } = crypto;

const callUserMutation = async (mutationInput) => {
    try {
        const { id, name, age, isDeleted } = mutationInput;
      const query = `
      mutation MyMutation(
        $age: Int!, 
        $id: String!, 
        $isDeleted: String!, 
        $name: String!
      ) {
        createUser(
            age: $age, 
            id: $id, 
            isDeleted: $isDeleted, 
            name: $name
        ) {
          body {
            age
            id
            isDeleted
            name
          }
          message
          status
        }
      }`;

      const APPSYNC_MUTATION_URL = 'your-appsync-mutation-url';
      const signer = new SignatureV4({
        credentials: defaultProvider(),
        region: 'us-east-1',
        service: 'appsync',
        sha256: Sha256,
      });
  
      const { host, pathname } = new URL('your-appsync-mutation-url');
      const params = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Host: host,
        },
        body: JSON.stringify({
          query,
          variables: {
            age,
            id,
            isDeleted,
            name,
          },
          operationName: 'MyMutation',
        }),
        hostname: host,
        path: pathname,
      };
      const requestToBeSigned = new HttpRequest(params);
      console.log('[INFO] requestToBeSigned', requestToBeSigned)
  
      // Sign the request to call mutation
      const signedRequest = await signer.sign(requestToBeSigned);
  
      // sending the signed request using Axios to call mutation.
      const response = await axios({
        url: APPSYNC_MUTATION_URL,
        method: signedRequest.method,
        data: signedRequest.body,
        headers: signedRequest.headers,
      });
      const { data } = response.data;
      console.log('[INFO] data from the mutation', data);
      return data;
    } catch (error) {
      console.log('[ERROR] Error while calling the mutation', JSON.stringify(error));  
      return error;
    }
};

module.exports.handler = async (event) => {
    try {
        const mutationData = callUserMutation({age: 22, id: '1', isDeleted: 'false', name: 'kishan'});
        console.log('[INFO] mutationData', mutationData);
    } catch (error) {
        console.log('[ERROR] Error',error);
        return error;
    }
}

```
## Breaking Down the Code:
after seeing the code does it look little confusion?, let understand the source code by splitting it.

- Initially, you must configure the parameters, such as the query, variables, method, and URL. which will be required to call the mutation.

```
      const { host, pathname } = new URL('your-appsync-mutation-url');
      const params = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Host: host,
        },
        body: JSON.stringify({
          query,
          variables: {
            age,
            id,
            isDeleted,
            name,
          },
          operationName: 'MyMutation',
        }),
        hostname: host,
        path: pathname,
      };
```
- Once you've made the parameters, the next step will be focusing on getting the authorisation token, that will allow the Lambda to invoke the mutation. To achieve this, use the **SignatureV4()** functionality, initiating the **signature.sign()** process. This will return an authorisation code which can be integrated with your parameters so that it as access to call the mutation.

```
      const signer = new SignatureV4({
        credentials: defaultProvider(),
        region: 'us-east-1',
        service: 'appsync',
        sha256: Sha256,
      });

      // Sign the request to call mutation
      const signedRequest = await signer.sign(requestToBeSigned);
```
- After the IAM authentication token has been added to your parameters, the next step involves utilising Axios to initiate the mutation call and retrieve the response. The following code will be doing the task.
```
      // sending the signed request using Axios to call mutation.
      const response = await axios({
        url: APPSYNC_MUTATION_URL,
        method: signedRequest.method,
        data: signedRequest.body,
        headers: signedRequest.headers,
      });
```

## Conclusion
By following these steps, now you are aware on how to call the appsync mutation from the Lambda with IAM security protection.