「API GatewayからAppSyncへ乗り換えてみよう」で書いたAppSyncの特徴としてPub/Subがありますよね。リアルタイムのサービスを作る場合には非常に重宝します。知りたいデータに関する追加、更新、削除用のMutationをアプリ側でSubscribeしておけば、ユーザ同士の操作がリアルタイムにアプリ上で見ることができますね。
それでは、バッグエンド側で行った操作をすぐにユーザ側に届けたいという場合にはどうしたらいいでしょう?例えば、システム側で定期的にデータの変更を行ったり、ユーザからの回答を返すようなサービスを作りたい場合とかですね。AppSyncを通常のHTTP通信で呼び出すことで実現できるんです。今回はLambdaからpython requestsを使った呼び出しについて説明します。
Layersにrequestsを登録
Lambda内でPythonのHttp通信ライブラリであるrequestsを使うんですが、せっかくなので昨年新しく登場したLayersを使ってみましょう。Lambda > LayerでLayerを作成します。今回必要となるライブラリは requests, certifi, chardet, idnaなのでこれらをZipで圧縮したファイルをアップロードします。圧縮するライブラリはpythonディレクトリの中にいれpythonディレクトリごと圧縮してください。登録したLayerをLambdaに追加すればいいだけなので簡単ですね。

AppSyncのエンドポイント取得
AppSyncのエンドポイントは、AppSyncのSettingsに書かれています。このエンドポイントがrequestsで与えるURLになります。lambda内のPythonコードに直接書いておいてもいいし、環境変数から与えたりしてもいいです。なお、このエンドポイントは、次のような構造になっているので、任意文字列箇所とリージョン名を与えることで合成することができます
https://任意文字列.appsync-api.リージョン名.amazonaws.com/graphql

HTTP送信
今回はAPIキーによる認証およびMutationの場合について説明します。そして、AppSyncコンソールQueriesにて下記のようなmutationのテストが完了している状態とします。これは、入力としてDeleteInputを与え、idが返却されるMutationですね
mutation deleteInfo($deleteinput: DeleteInput!) { deleteInfo(input: $deleteinput) { id } }
HTTPのbodyをKey:Value形式として、"query" Keyに対しValueに呼び出すAppSyncのMutationを文字列で記述します。そして、入力であるDeleteInputの中身については、"variables" KeyのValueに記述します。"variables"のValueは、文字列でなくてもOKです。そしてこれらをjson.dumpsし、JSON形式にエンコードします。
body_json = { "query": "mutation deleteInfo($deleteinput: DeleteInput!) { deleteInfo(input: $deleteinput) { __typename id } }", "variables":{ "deleteinput": { "id": id } } } body = json.dumps(body_json)
APIキー認証の場合は、APIキーをheadersにKey:Value形式で設定します。これらをrequests.requestの引数として与え作成したLambdaを呼び出すと、このMutationをクライアントアプリから呼び出した時と同じログがCloudWatchに出力され、Subscribeしているユーザに、無事変更内容が通知されます。
headers = {"x-api-key":xxxxxxxxxxxxxxx, "Content-Type":"application/graphql"} response = requests.request('POST', url, data=body, headers=headers)
HTTP戻り値受信
返却されたresponse内の"_content"にバイナリとして戻り値が返ってきます。
{'_content': b'{"data":{"deleteInfo":{"__typename":"Info","id":"e86007cc-8e5b-429a-9e69-4ea81fccd753"}}}'}
返却されたresponse内の"_content"にバイナリとして戻り値が返ってきますので、デコードを行ったオブジェクトから値を抽出します。
content = response.__dict__['_content'] str_content = content.decode('unicode-escape').encode('latin1').decode('utf-8') obj = json.loads(str_content) if 'data' in obj: id = obj['data']['deleteInfo']['id']
今回のようにMutationの場合は、戻り値まで取得する必要があまりないですが、queryを実行した場合は必要ですね。ただし、AppSyncのqueryをHTTPで呼び出すぐらいなら、DBを直接読んだ方が楽なような気がしますが・・・。