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