Using AppSync Client from Lambda

Access your Amplify generated AppSync GraphlQL API from a lambda

Mon, 06 Jul 2020

It took me a while to figure out how to use the Amplify GraphQL client in a lambda so I can access my AppSync API. The main reason I wanted to use this is so I can invoke subscription updates to my frontend. If you’re interested in making signed https request, check out this tutorial.

Code

npm install aws-appsync es6-promise graphql-tag isomorphic-fetch

The env variables should all be provided by Amplify.

/* eslint-disable no-console */
require("es6-promise").polyfill();
require("isomorphic-fetch");
// eslint-disable-next-line import/no-extraneous-dependencies
const AWS = require("aws-sdk");
const AWSAppSyncClient = require("aws-appsync").default;
const { AUTH_TYPE } = require("aws-appsync");

const region = process.env.REGION;
AWS.config.update({
  region
});
const appsyncUrl = process.env.API_RADLOOP_GRAPHQLAPIENDPOINTOUTPUT;
const gql = require("graphql-tag");

// graphql client.  We define it outside of the lambda function in order for it to be reused during subsequent calls
let client;

// used to parse different types of query results to return only the item.
function parseResults(operationName, data) {
  if (operationName.includes("List")) {
    return data[`l${operationName.substring(1, operationName.length)}`];
  }
  if (operationName.includes("GetOrders")) {
    return data[`g${operationName.substring(1, operationName.length)}`];
  }
  return data[operationName];
}

// initializes our graphql client
function initializeClient() {
  client = new AWSAppSyncClient({
    url: appsyncUrl,
    region,
    auth: {
      type: AUTH_TYPE.AWS_IAM,
      credentials: AWS.config.credentials
    },
    disableOffline: true
  });
}

// generic mutation function.  A way to quickly reuse mutation statements
async function executeMutation(mutation, operationName, variables) {
  if (!client) {
    initializeClient();
  }

  try {
    const response = await client.mutate({
      mutation: gql(mutation),
      variables,
      fetchPolicy: "network-only"
    });
    return parseResults(operationName, response.data);
  } catch (err) {
    console.log("Error while trying to mutate data");
    throw JSON.stringify(err);
  }
}

// generic query function.  A way to quickly reuse query statements
async function executeQuery(query, operationName, variables) {
  if (!client) {
    initializeClient();
  }
  try {
    const response = await client.query({
      query: gql(query),
      variables,
      fetchPolicy: "network-only"
    });
    return parseResults(operationName, response.data);
  } catch (err) {
    console.log("Error while trying to fetch data");
    throw JSON.stringify(err);
  }
}

exports.handler = async event => {
  console.log(event); // logging so you can see what gets passed when you invoke it

  // updating a users firstName
  await executeMutation(
    `mutation updateUser($id: ID!) {
        updateUser(id: $id) {
      id
      firstName
     }
   }`,
    "updateOrder",
    {
      input: {
        id: "1234",
        firstName: "Bob"
      }
    }
  );

  // querying the user that we changed
  const user = await executeQuery(
    `query getUser($id: ID!) {
    getUser(id: $id) {
      id
      fullName
      firstName
      lastName
     }
   }`,
    "updateOrder",
    {
      id: "1234"
    }
  );

  return user;
};

If you have trouble with permissions make sure the schema that you are accessing has lambda permissions as well as your lambda has permissions to access that schema.

Permissions for the schema

Example schema. This will allow cognito userpools as well as IAM(for lambda)

type User
@model
@auth(
 rules: [
    {
      allow: private
      provider: userPools
      operations: [read, update, create]
    }
    {
      allow: private
      provider: iam
      operations: [read, update, create, delete]
    }
  ]
) {
  id: ID!
  firstName: String
}

Run an amplify push after

Permissions for the lambda

In your terminal at the root of your amplify directory:

  1. amplify function update
  2. Select function that this code is in
  3. Let this function access other resources
  4. Select GraphQL/Appsync option
  5. Regardless of what this function does, give it Create, Update and Read. AWS is strange and your lambda requires these permissions to be able to access the AppSync API even if your lambda may not be using the those permissions.
  6. amplify push
Loading...
Edward Beazer

Edward Beazer - I just like to build shit. Sometimes I get stuck for hours, even days while trying to figure out how to solve an issue or implement a new feature. Hope my tips and tutorials can save you some time.