'Failing to call GraphQL from Lambda using Amazon Amplify

I'm using Amazon Amplify to create a function that accesses the contentment of an AppSync GraphQL endpoint. Using the command line I create the new function and API and I can call it correctly from my front end application, however, I keep receiving this error:

2020-12-16T01:39:40.524Z    979abb8d-4d64-4929-937a-03b0cb495174    INFO    error posting to appsync:  Error: Request failed with status code 401
    at createError (/var/task/node_modules/axios/lib/core/createError.js:16:15)
    at settle (/var/task/node_modules/axios/lib/core/settle.js:17:12)
    at IncomingMessage.handleStreamEnd (/var/task/node_modules/axios/lib/adapters/http.js:244:11)
    at IncomingMessage.emit (events.js:326:22)
    at endReadableNT (_stream_readable.js:1223:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {
  config: {
    url: 'https://y63v24inqneirizeuiig7x5g54.appsync-api.us-east-1.amazonaws.com/graphql',
    method: 'post',
    data: '{"query":"query listCompetitions {\\n  listCompetitions {\\n    items {\\n      createdAt\\n      id\\n      likes\\n      updatedAt\\n      votes\\n      websiteImageKey\\n      websiteUrl\\n      user {\\n        username\\n        imageKey\\n      }\\n      websiteDescription\\n    }\\n  }\\n}\\n"}',
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',
      'x-api-key': '7aeiasrvb5bczhmlt2nnbpfuyi',
      'User-Agent': 'axios/0.21.0',
      'Content-Length': 284
    },

The code I'm using to call GraphQL from my function is this from this link.

const graphqlData = await axios({
      url: process.env.API_MAINDATA_GRAPHQLAPIENDPOINTOUTPUT,
      method: "post",
      headers: {
        "x-api-key": process.env.API_MAINDATA_GRAPHQLAPIIDOUTPUT,
      },
      data: {
        query: print(listCompetition),
      },
    });

The URL and KEY is added to the env by Amplify, I already added API_KEY auth for the API in question, so I can't understand why I still get 401 error.

One thing I notice is that the document says to use:

'x-api-key': process.env.API_<YOUR_API_NAME>_GRAPHQLAPIKEYOUTPUT

However _GRAPHQLAPIKEYOUTPUT is not been generated so I use _GRAPHQLAPIIDOUTPUT instead, not sure if this is something that changed or if I'm missing an env constant.



Solution 1:[1]

Are you signing your request properly? Check out AWS.Signers.V4 if you haven't already.

I ran into a similar problem as you, and I ended up implementing a dedicated Lambda function to execute GraphQL operations. That way, I can just invoke my Lambda function whenever I want to execute a GraphQL operation.

Something like:

/* Amplify Params - DO NOT EDIT
    API_XXXXXX_GRAPHQLAPIENDPOINTOUTPUT
    API_XXXXXX_GRAPHQLAPIIDOUTPUT
    ENV
    REGION
Amplify Params - DO NOT EDIT */

const AWS = require('aws-sdk')
const https = require('https')
const urlParse = require('url').URL

const APPSYNC_URL = process.env['API_XXXXXX_GRAPHQLAPIENDPOINTOUTPUT']
const REGION = process.env['REGION']

const ENDPOINT = new urlParse(APPSYNC_URL).hostname.toString()

//
// execute a graphql operation via appsync
//
exports.handler = async ({ item, operation, operationName }) => {
  const request = createSignedRequest(
    ENDPOINT,
    item,
    operation,
    operationName,
    REGION,
    APPSYNC_URL,
  )
  return getResponseFromApi(ENDPOINT, request)
}

//
// create a signed graphql operation request
//
const createSignedRequest = (
  endpoint,
  item,
  operation,
  operationName,
  region,
  url,
) => {
  const request = new AWS.HttpRequest(url, region)
  request.method = 'POST'
  request.path = '/graphql'
  request.headers.host = endpoint
  request.headers['Content-Type'] = 'application/json'
  request.body = JSON.stringify({
    query: operation,
    operationName: operationName,
    variables: item,
  })

  const signer = new AWS.Signers.V4(request, 'appsync', true)
  signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate())

  return request
}

//
// send a request to the appsync api and return the response data
//
const getResponseFromApi = (endpoint, request) => {
  return new Promise((resolve, reject) => {
    const httpRequest = https.request(
      { ...request, host: endpoint },
      (result) => {
        result.on('data', (data) => {
          resolve(JSON.parse(data.toString()))
        })
      },
    )
    httpRequest.write(request.body)
    httpRequest.end()
  })
}

Solution 2:[2]

GRAPHQLAPIKEYOUTPUT is only generated if you add API Key as an authorization mode to your api category.

Once you do that, add apikey as the auth mode for the GraphQL Model you're trying to access. Update the function and it will re-create the variables including a new one called GRAPHQLAPIKEYOUTPUT.

Then the sample code should work.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ben Rosen
Solution 2 DJSjr