Using the AppSync REST API from a Lambda

Learn how to sign a https request to access your appsync rest api

Tue, 07 Jul 2020

The Amplify docs provide some detail on how to access your API from a lambda. This method will grab the authorization token that is passed from the users GraphQL client and use it to make the request.

If you wanted to learn how to use the AppSync client in your lambda directly, check out this tutorial.

Code

Prereqs

npm install https url

The env variables should all be provided by Amplify.

This example will grab the users auth token as well as the argument username from the event object that gets passed when AppSync invokes your lambda. We will use the username which is of type ID to make a query in AppSync to grab the users first, last and fullName.

const https = require("https");
const AWS = require("aws-sdk"); // eslint-disable-line
const UrlParse = require("url").URL;
const appsyncUrl = process.env.API_RADLOOP_GRAPHQLAPIENDPOINTOUTPUT;
const region = process.env.REGION;
const endpoint = new UrlParse(appsyncUrl).hostname.toString();

async function executeQuery(
  query, // The graphl query we want to run
  operationName, // the name of the query. ex: getUser
  variables, // variables object.  ex: { id: "1234" }
  authorization, // users auth token
) {
  const req = new AWS.HttpRequest(appsyncUrl, region);
  req.method = "POST"; // IMPORTANT!!  All graphql requests(even queries) are POST request.  Lost a few hours to figure that one out
  req.headers.host = endpoint;
  req.headers["Content-Type"] = "application/json";
  req.body = JSON.stringify({
    query,
    operationName,
    variables
  }); // turning our query into a http request body string
  if (authorization) {
    req.headers.Authorization = authorization;
  } else {
    context.fail(null, "No authorize token was provided in header");
  }
  console.log(`Executing query ${operationName}`);
  /*
   * This is the request portion.  Initially we create an array that will
   * hold chunks of data for us since our request will come back in a few
   * pieces if it is long.  The Promise we use below will wait for the
   * request to finish by listening to two streams.  The first stream is
   * data which fires off when a chunk of data is returned back to us.
   * The chunk is added to our dataChunk array.  The second stream we listen
   * to is the end stream.  End means that all of the data has been sent to
   * us.  We then combine our data stream using Buffer.concat, parse our
   * returned data into JSON and check to see if there are any errors.
   * If there aren't any errors we return the data.  Normally the response
   * is in a format of "response.data.operationName".  To clean it up, we
   * just return "response.data[operationName]" which in our case would be
   * the getUser object.
   */
  const dataChunks = [];
  // eslint-disable-next-line
  return await new Promise((resolve, reject) => {
    const httpRequest = https.request({ ...req, host: endpoint }, result => {
      result
        .on("data", data => {
          dataChunks.push(data);
        })
        .on("end", () => {
          const responseData = Buffer.concat(dataChunks);
          const response = JSON.parse(responseData.toString());
          if (response.errors) {
            console.log(`Failed during ${operationName} query`);
            console.log("Request body: ", req.body);
            console.log(response.errors);
            reject(null, `Failed during ${operationName} query`);
          }
          resolve(response.data[operationName]);
        });
    });
    httpRequest.write(req.body);
    httpRequest.end();
  });
}
exports.handler = async (event) => {
    const {
      arguments: {
        input: { 
          username
        }
      },
      request: {
        headers: {
          authorization // auth token passed from the user invoking the lambda
        }
      }
    } = event;
  
  try {
    const user = await executeQuery(
      `query getUser($id: ID!) {
         getUser(id: $id) {
           id
           fullName
           firstName
           lastName
          }
        }`,
        "getUser",
        {
         id: username
        },
        authorization
      );
    return user;
  } catch (err) {
    console.log(err)
    return null;
  }
};
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.