DEV Community

Kishan
Kishan

Posted on • Edited on

AWS IAM Auth: Calling AppSync Mutations from Lambda (Step-by-Step-Guide)

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
        }
      }
Enter fullscreen mode Exit fullscreen mode

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"
            ]
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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;
    }
}

Enter fullscreen mode Exit fullscreen mode

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,
      };
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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,
      });
Enter fullscreen mode Exit fullscreen mode

Conclusion

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

Top comments (0)