Lambda@EdgeからとあるエンドポイントにGraphQLのqueryを実行したい場合がありました。
Lambda@Edgeにはレイヤーをアタッチすることができないので、Lambdaのランタイムに組み込まれている方法でhttpリクエストを飛ばす必要があります。
(正確にはコードとライブラリをまとめたzipが1MB以下に収まるのであれば、そのzipをデプロイすることでサードパーティのNodeライブラリも利用することができるようになりますが、実装時点では調査が足らず不可能だと思っていました。詳しくは下のリンクをご参照ください。)
今回はとある事情から、ランタイムとしてNode.js
のv16系を利用していました。
Nodeのhttpクライアントライブラリとしてはaxios
やnode-fetch
などがよく使われますが、Node16にこれらは組み込まれていません。
Node.js
のv18系を使えるなら、fetchがデフォルトで使えるためそちらを使った方が良いでしょう。
Node.js
のv16系では唯一https
のみが組み込みで使えますが、それを利用した場合のGraphQLのquery実行の方法が分かりづらかったので、メモの意味合いも兼ねて記事にしようと思います。
いきなりコードを共有
エラーハンドリングなどは適当な部分もありますので、参考にする際は適宜処理を追加して下さい。
"use strict";
const https = require("https");
const options = {
hostname: "hogepiyotest.com", // リクエスト先のホスト名を指定
path: "/",
port: 443,
method: "POST",
headers: {
"Content-Type": "application/json",
},
};
// 実行したいQuery
const infoQuery = `
query Info($id: ID!) {
info(id: $id) {
id
description
}
}
`;
const getInfo = (operation, id) => {
const requestBody = JSON.stringify({
operationName: operation,
variables: {
id: id,
},
query: infoQuery,
});
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let body = "";
res.on("data", (d) => {
body += d;
});
res.on("end", () => {
console.log(JSON.parse(body).data);
resolve(JSON.parse(body).data);
});
});
req.on("error", (error) => {
console.error(error);
reject(new Error(error));
});
req.write(requestBody);
req.end();
});
};
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const path = request.uri;
// 今回はアクセスされたURLのパスの一番目に、欲しい情報に対応するIDを指定すると仮定
const pathList = path.split("/");
const id = pathList[1];
const fetchedData = await getInfo("info", id);
// 以下、受け取った内容をご所望の形に整形してください
...
};
ざっくり解説
今回のミソは、https
を使ってGraphQLのリクエストをどのように投げれば良いのか、ということに尽きます。
こちらを参考に実装しましたが、少しだけ解説します。
getInfo
という関数がGraphQLのリクエストの役割を担っており、その中でPromiseを利用した非同期処理を行います。
Promiseの中にあるhttps
を使ったリクエストがその非同期処理の正体です。
ここでGraphQLのリクエストについて簡単に説明します。
GraphQLのリクエストで送信できるデータは、query
、operationName
、variables
の3つです。
query
は必須で、GraphQLクエリを記述します。GraphQLは単一のエンドポイントを介して機能するため、エンドポイントが応答するデータはすべてクエリに依存します。
variables
はオプションで、クエリに渡される変数の値を含むJSONオブジェクトです。例えば、クエリにidという変数が必要な場合(クエリにおいては$idと表示される)、以下のように変数を送信する必要があります。
{
"id":1
}
今回はこのidの値を可変にするためにgetInfo関数でidを受け取れるようにしています。
operationName
も省略可能です(今回は明示しています)。これは、複数の名前のついたオペレーションを含むクエリがある場合に、どのオペレーションを実行するかを指定するために使用されます。
この3つのパラメータをrequestBody
として定義し、getInfo
の中でエンドポイントにリクエストしています。
あとはこのgetInfo
をawaitを用いて呼び出すことで、Promiseの中身を取り出すことができます。
今回は受け取った中身の加工部分の処理は特に記述していませんが、あとは好きなように調理するだけです。
参考