【AWS】Amplifyで作成されたAppSyncのGraphQLを外部から発動したい。(API Gateway → Lambda → AppSync → DynamoDB)
概要
AWS Amplifyを用いるとCLIのおかげで、コマンド実行するだけでAWSのいろんなサービスをよしなに構築してくれて、難しいことはあまり考えなくてもアプリケーションが作れる。
とりあえず公式ドキュメントに倣って、API(GraphQL), Authentication, の機能を作成することが容易にできた。
amplify-cliのコマンドを実行するだけで、Cognito, AppSync, DynamoDBを構築してくれている。AWSでサービスを組み合わせるのをやってくれるのは非常にありがたい。(コンソール画面での設定の仕方とかも入力項目が多くて正直よくわからないから)
https://docs.amplify.aws/lib/graphqlapi/getting-started/q/platform/ios
https://docs.amplify.aws/lib/auth/getting-started/q/platform/ios
そんな中、勝手に構築してくれたAppSyncのGraphQLをCognito認証がない外部アプリからもやりたくなった。
とりあえず、Lambdaを使えばいいんだろうことは想像できたけど詳しいことは調べないとわからなかった。AppSyncでIAMでの認可するのもどうやるのか調べながらだった。
目次
長くなりそうなので目次です。
- 概要
- 目次
- 大まかな流れ
- AppSyncのAPI設定で認証プロバイダーにIAMを設定
- GraphQLのスキーマを変更
- Lambda関数を作成
- API Gatewayを作成
- 動作を確認しよう
- まとめ
- 参考ページ一覧
大まかな流れ
1. まず、AppSyncのAPI設定で認証プロバイダーにIAMを設定する。
(amplify-cliでやった時にcognitoをデフォルトで設定したので、IAMでもいけるようにする)
2. AppSyncでGraphQLのスキーマを変更
(スキーマに追加した認証プロバイダーに関する追記を行う)
3. AppSyncのAPI(GraphQL)にリクエストを送るLambda関数(Node.js)を作成する。
(amplify-cliで作成されたcreateUserとかを実行させてDynamoDBを操作する)
4. 好きなところからLambda関数を呼び出すためにAPI Gatewayを作成する。
(Lambdaを実行するトリガーにAPI Gatewayを設定する)
ほぼ全部AWSのコンソール画面で操作します。
好きな順番でやっていいも大丈夫だと思います。(2の前に1は必須)
AppSyncのAPI設定で認証プロバイダーにIAMを設定
AWS AppSyncより作成したAPIを選択して左ナビの設定から設定画面へ行きましょう。
追加の認証プロバイダーより、Newを押して表示されるダイアログで追加するプロバイダーを設定します。
参考: Cognito認証されているAppSyncをIAM認証で後ろから叩く - Qiita
GraphQLのスキーマを変更
認証プロバイダーにIAMを追加しましたが、このままではデフォルトで設定されているものしか使用されないので、GraphQLのスキーマを変更します。
同じAppSync画面の左ナビのスキーマからスキーマ編集画面へ行きましょう。
外部から実行したいクエリに以下のように追記します。
※ スペースを入力するとエラーが何故かエラーが出ることがありますのでコピペ安定かもです。
# Before type Mutation { createUser(input: CreateUserInput!): User updateUser(input: UpdateUserInput!): User ... }
# After type Mutation @aws_cognito_user_pools @aws_iam { createUser(input: CreateUserInput!): User updateUser(input: UpdateUserInput!): User ... }
記述の意味は、Mutationを実行するのにCognitoとIAMを認証プロバイダーとして使用するというものです。
Beforeのなにも書いてない状態はデフォルトが反映されています。(僕の場合はcognitoがデフォルトなので@aws_cognito_user_poolsがデフォルトでついて記述されているようなもの)
記述しない場合がデフォルトというだけなので、記述する場合はデフォルトのもの含めて設定したいものを全て記述する必要があります。
今回はMutationだけでいいのでこれだけしか編集していませんが、Queryにも同様に設定したり個別に設定できます。
# MutationのcreateUserはCognitoとIAM、updateUserはIAMだけ type Mutation { createUser(input: CreateUserInput!): User @aws_cognito_user_pools @aws_iam updateUser(input: UpdateUserInput!): User @aws_iam ... } # Queryはcognitoだけ type Query @aws_cognito_user_pools { getUsers(id: ID!): User listUsers(filter: ModelUsersFilterInput, limit: Int, nextToken: String): ModelUserConnection ... }
編集後は画面右上の「スキーマを保存」を忘れずに押してください。
参考: Cognito認証されているAppSyncをIAM認証で後ろから叩く - Qiita
Lambda関数を作成
関数を新規作成
一から作成で設定はいじらずに作成します。
関数にポリシーを付与
次に、LambdaがAppSyncのGraphQLを実行できるように、作成した関数にAppSyncのポリシーを付与します。
関数のアクセス権限タブを選択して、実行ロールからロールを以下のように編集します。
関数を実装
ポリシーを付与したら、関数を作成します。index.jsを直接編集してDeployを押せば良いです。
作成した関数は以下のような感じです。
こちらが非常に参考になりました。↓
AlexaのPersistent AttributesをAppSyncからGraphQLで取得する | WP Kyoto
require('isomorphic-fetch'); const aws = require('aws-sdk'); const AWSAppSyncClient = require('aws-appsync').default; const AUTH_TYPE = require('aws-appsync/lib/link/auth-link').AUTH_TYPE; const gql = require('graphql-tag'); const url = 'AppSyncの設定画面に記載されているAPI URL'; const region = 'ap-northeast-1'; const auth_type = AUTH_TYPE.AWS_IAM; // AWS_IAMの部分が認証プロバイダーです。cognitoならば、AUTH_TYPE.AMAZON_COGNITO_USER_POOLS async function createUser(data) { const variables = { "createUserInput": { // ここのcreateUserInputという名前は、以降にあるcreateUserGqlの$createUserInputと同じ名前にする必要があります。 // graphQLスキーマにcreateUser(input: CreateUserInput!): Userとあったので、 // ここにはCreateUserInputと同じ構造で記述する。 // ~~Inputは編集したAppSyncのスキーマに書かれています。 "username": data.username } } const createUserGql = gql(` mutation createUser($createUserInput: CreateUserInput!) { // さっきのvariables内にあったcreateUserInputがここに入る createUser(input: $createUserInput) { username } }` ); const client = new AWSAppSyncClient({ url: url, region: region, auth: { type: auth_type, credentials: aws.config.credentials }, disableOffline: true }); try { const result = await client.mutate({ mutation: createUserGql, variables }); console.log(JSON.stringify(result)); return { status: "OK", result: result }; } catch (err) { console.log(JSON.stringify(err)); return { status: "Error", result: err }; } }; /** * リクエストのbodyは以下のようなものを想定しています。 * { "mutation": "createUser", * "data": { "username": "ユーザーメイ" } * } */ exports.handler = async (event, context) => { let body; let statusCode = 200; const headers = { 'Content-Type': 'application/json', }; try { switch (event.httpMethod) { case 'DELETE': throw new Error(`Unsupported method "${event.httpMethod}"`); case 'GET': throw new Error(`Unsupported method "${event.httpMethod}"`); case 'POST': const bodyJson = JSON.parse(event.body); if (bodyJson.mutation == 'createUser') { const res = await createUser(bodyJson.data); body = res; } else { throw new Error(`Unsupported mutation "${bodyJson}". Invalid request parameters "${bodyJson}"`); } break; case 'PUT': throw new Error(`Unsupported method "${event.httpMethod}"`); default: throw new Error(`Unsupported method "${event.httpMethod}"`); } } catch (err) { statusCode = '400'; body = err.message; } finally { body = JSON.stringify(body); } return { "statusCode": statusCode, "headers": headers, "body": body }; };
node_modulesが必要
ただ、このままでは機能しません。
requireで読み込むモジュールを用意しなければなりません。(npm installしたnode_modules)
この手順のみAWSコンソールの操作ではないです。
まず、適当な場所にnodejsという名前でフォルダを作成。
次に、先ほどのindex.jsでrequireしているものをnpm install する(isomorphic-fetch, aws-sdk, aws-appsync, graphql-tag)
※ aws-appsyncはversionを@^1.0.0した方がいいです。そのまま最新バージョンをinstallしたら
require('aws-appsync/lib/link/auth-link').AUTH_TYPE;
できませんでした。
そして、nodejsを圧縮してnodejs.zipを作成します。
これを作成したlambda関数のレイヤーとして設定します。
こちらが非常に参考になりました↓
node_modulesをLambdaレイヤーにアップロードしてライブラリを使用する - Qiita
API Gatewayを作成
最後にLambda関数のトリガーを追加からAPI Gatewayを設定すれば完成です。
トリガーの追加から設定すると勝手にAPI Gatewayを作成してLambda関数と紐づけてくれます。
API Gatewayが作成され、GET, POST等の全てに対応したAPIが作成されます。
作成されたものを特にいじる必要はないと思います。(リクエストのパラメータとかいろいろ調整したい場合は別)
セキュリティでAPIキーを選択すると、APIキーが作成されAPIとキーを紐付けるのも自動でやってくれています。
動作を確認しよう
APIのURLは、左ナビのステージよりURLの呼び出しと書いてあるのがURLです。
とりあえずcurlなどで動作確認してみましょう。
今回の場合は以下のようになります。
curl -H "x-api-key: 作成されたAPIキー" -X POST -d'{ "mutation": "createUser", "data": { "username": "ユーザーメイ" } }' 作成されたAPI GatewayのAPI URL
まとめ
AWSは一つ一つのサービスを設定するだけならそんなに難しくはならないけど、必ず複数のサービスを紐づけて使用することになるのでポリシー等の設定がよくわからなくなりがちになります。
今回調べたことは直接何かで使えるかは分かりませんが、AWSでよく使われているであろうLambdaとAPI Gatewayを勉強できたのは面白かったです。
よく使うサービスは少しずつできるようになっていきたいです。
参考ページ一覧
Amplify公式ドキュメント
https://docs.amplify.aws/lib/graphqlapi/getting-started/q/platform/ios
https://docs.amplify.aws/lib/auth/getting-started/q/platform/ios
AppSyncの複数認証
Cognito認証されているAppSyncをIAM認証で後ろから叩く - Qiita
AppSyncのGraphQLを実行するLambda実装
AlexaのPersistent AttributesをAppSyncからGraphQLで取得する | WP Kyoto
node_modulesをLambdaレイヤーにアップロードしてライブラリを使用する - Qiita