🍅

AWS AppSyncのHTTPデータソース(+JSリゾルバー)で、他AWSサービスと直接連携する

2023/08/16に公開

はじめに

AWS AppSyncと他のAWSサービスと連携したいとき、Lambdaを間に挟んでAWS SDKを使ってやり取りするのが普通じゃないかなと思います。(DynamoDBのように直接呼び出す仕組みが用意されているサービスは除く)
しかし、今回はLambdaを使わずにAppSyncからAWSのAPIを呼び出すことで他のAWSサービスを直接連携したいと思います。

すなわち、JSリゾルバーのドキュメントにさらっと書いてあるこの機能を実装します。

(公式の日本語訳ページは意味不明だったのでDeepLで翻訳しました)

この記事やることを、もう少し具体的に

技術的な繋がりがほとんど無いですが、一応前回記事の続きになります。
https://zenn.dev/ibaraki/articles/77c27207c813dc
この記事ではIoT EventsでTodoリストっぽいものを作りました。しかし、インターフェースがなくてそのままだとWebアプリ化出来ないので、AppSyncでAPI化したいと思います。
リソースをなるべく増やしたくないので、Lambdaは挟まずにAppSyncからIoT Eventsを直接呼び出したいと思います。
この記事はAppSyncからIoT Eventsの連携をベースに説明しますが、同じ方法で他AWSサービスとも直接連携が可能です。

実際に構築するための主な参考ドキュメント

https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/tutorial-http-resolvers.html#invoking-aws-services

実際に構築する

IoT Eventsのディテクターの一覧を取得するAPIをAppSyncで作ります。

とりあえず、適当にAppSyncのリソースを作る

Webのコンソールからでよいので、適当にAppSyncを作ります。
データソースはこの後CLIで作るので、コンソールからは作らないでください。

IoT EventsのAPIを実行するHTTPデータソースを作る

そもそもAWSに対する操作は基本的にAPIを呼び出して実施することが出来ます。
コンソールからの操作もCLIからの操作もSDKからの操作も、裏では全てAPIを呼び出しています。ですので、適切にAPIさえ呼び出せればLambdaとかを使わなくてもAWSリソースの操作は可能なわけです。
つまりAppSyncからAPIが呼べれば良いわけですが、AppSyncにはAPIを呼び出せるデータソースがあります。それがHTTPデータソースです。

AWSのAPI仕様を確認する

AWSのドキュメントを確認すると、ListDetectors APIを呼び出すことで、今回の目的が達成できる事がわかります。
https://docs.aws.amazon.com/iotevents/latest/apireference/API_iotevents-data_ListDetectors.html

あとは定義どおりに呼び出せばよいわけですが、普通にAPIを呼ぶと認可エラーになってしまいます。
認可を通す為の設定が必要になります。

ロールの作成

今回は前述の通りAPIによりIoT Eventsと連携為にHTTPリゾルバーを使います。
HTTPリゾルバーに適切な権限を付けるために、まずは、IAMでロールを作ります。
IAMロールには、以下のポリシーをアタッチしておきます。

ポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iotevents:ListDetectors",
                "iotevents:DescribeDetector"
            ],
            "Resource": "*"
        }
    ]
}

ListDetectorsが必要なので、それを許可しています。
将来的にディテクターの詳細も確認したいので、DescribeDetectorも付けておきました。

HTTPデータソースの定義用のjsonを作る

正直良くわかってないのですが、定義用のjsonが必要なので作ります。
AWSのAPIの認可を突破するにはSigV4署名というものが必要なのですが、その署名の為の情報を入れる必要があります。

iot.json
{
  "endpoint": "https://data.iotevents.ap-northeast-1.amazonaws.com",
  "authorizationConfig": {
    "authorizationType": "AWS_IAM",
    "awsIamConfig": {
      "signingRegion": "ap-northeast-1",
      "signingServiceName": "ioteventsdata"
    }
  }
}

signingServiceNameの値が分からなくて6回くらい作り直しました。(ioteventsでもdata.ioteventsでも無くてioteventsdata...分からんよ)
ServiceNameの一覧ってどこかで確認できたりするのでしょうか?

cliからHTTPデータソースを作る

jsonが出来たら、cliからHTTPデータソースを作ります。(webの画面からだとroleやjsonの指定ができないので、cliから作ってください)

aws appsync create-data-source --api-id {appsyncのid} --name {データソース名} --type HTTP --http-config file://iot.json --service-role-arn {さっき作ったロールのarn}

できました。

webの画面上で見るとただのhttpデータソースに見えますが、裏で認可の設定とかが入っているので、AWSのセキュリティを突破してIoT Eventsと連携することができます。

スキーマを作る

スキーマを作ります。

type Task {
	id: ID!
	title: String
	done: Boolean
}
type Query {
	getTaskList: [Task]
}
schema {
	query: Query
}

パイプラインリゾルバーを作る

画面の右側からgetTaskListを探し「アタッチ」を押下すると、パイプラインリゾルバーを作ることが出来ます。

リゾルバーコードはデフォルトのままでOKです。
関数を追加して、そちらに必要なコードを書いていきます。

関数(JSリゾルバー)を書く

関数を追加したら、まず、データソースに先程cliで作ったHTTPデータソースを選択します。

その後、関数コードを書きます。以前はAppSyncのリゾルバーはVLCというイマイチマイナーな言語で書く必要があり敷居が高かったですが、今では関数を使うことでJavaScriptでも書くことが出来ます。

ドキュメントを読むとリソースのSyntaxは、

GET /detectors/detectorModelName?maxResults=maxResults&nextToken=nextToken&stateName=stateName HTTP/1.1

なので、リソースパスを/detectors/{ディテクター名}にします。queryパラメータは必要に応じて指定すると良いですが、全て任意なので今回は指定しませんでした。

import { util } from '@aws-appsync/utils';

export function request(ctx) {
    return {
        version: "2018-05-29",
        method: "GET",
        resourcePath: "/detectors/ibaraki-test-iot-events",
        params: {
            headers: {
                "Content-Type": "application/json"
            },
            query: {},
            body: JSON.stringify({})
        }
    }
}

export function response(ctx) {
    const { error, result, stash } = ctx;
    
    if (result.statusCode === 200) {
        const body = JSON.parse(result.body);
        return body.detectorSummaries.map((d) => {
            return {
                id : d.keyValue,
                done : (d.state.stateName == 'done')
            };
        });
    }else{
        util.appendError(result.body, result.statusCode);
    }
}

パイプラインリゾルバーに関数を追加する

関数を保存したら、パイプラインリゾルバーに設定するのを忘れないようにしましょう。

私は、セットするのを忘れて何も動かず、無駄に30分くらいエラーと戦っていました。

試す

webのコンソールのクエリから、試せるので試します。

ミスってなければ、無事にリストが取得できます。

まとめ

以下の手順でAppSyncと好きなAWSサービスを連携することが出来ます。

  1. 連携先のAWSサービスのAPI仕様を調べる
  2. 連携先のAWSサービスの実行を許可するRoleを作る
  3. HTTPデータソース定義用のjsonを作る
  4. cliからHTTPデータソースを作る
  5. パイプラインリゾルバーを作る
  6. 関数(JavaScriptリゾルバー)を作って、HTTPデータソースを実行するコードを書く
  7. パイプラインリゾルバーに関数を設定する

今後

今回JSリゾルバーを書いてみて、コンソールからだとJavaScriptが書きにくいと感じました。あとJSでなくてTSで書きたいです。CDKとかを使っていい感じに開発したいところですが、それは次の機会ということで、この記事は以上です。

書きました。CDK+TSでリゾルバーを書く。
https://zenn.dev/ncdc/articles/b7dc78a1669fca

NCDCエンジニアブログ

Discussion