この記事は約6分で読めます。
この記事は1年以上前に書かれたものです。
内容が古い可能性がありますのでご注意ください。
こんにちは。アプリケーションサービス部 河野です。
今回は、 GraphQL の @function ディレクティブを使用して Lambda リゾルバを構築してみました。
概要
Amplify CLI は いくつかの GraphQL ディレクティブを提供しています。
デフォルトスキーマにある @model もその内の一つです。
API (GraphQL) - Directives reference - AWS Amplify Docs
今回は、Lambda リゾルバを簡単に構築できる @function ディレクティブの使い方について紹介します。
Lambda リゾルバ
Lambda リゾルバは、AppSyncの Query または Mutation をAWS Lambda 関数にマッピングし、AppSync 経由で Lambda を実行することができる機能です。
https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/tutorial-lambda-resolvers.html
vtl を介さず直接 Lambda に処理を移譲できるダイレクト Lambda リゾルバもありますが、@function で作成されるものは、通常の Lambda リゾルバになります。
Lambda と ダイレクト Lambda リゾルバについては、以下のブログをご参照ください。
実践
以下のドキュメントを参考に実際に @function を試していきます。
API (GraphQL) - Custom business logic (Lambda function, HTTP, and VTL resolvers) - AWS Amplify Docs
※ amplify init は実行済みであるとします
1. 文字列を返す Lambda を呼び出す
GraphQL の Query から、文字列だけを返すシンプルな Lambda 関数を実行します。
1-1. GraphQL API を追加する
amplify add api で GraphQL API を追加します。
amplify add api? Select from one of the below mentioned services: GraphQL? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue? Choose a schema template: Blank Schema
schema.graphql を Query を追加します。
type Query {echo(msg: String): String @function(name: "echofunction-${env}")}
@function ディレクティブの後に、関数名をを加えます。
CLI から function(Lambda) を追加した場合は、"-{環境名}" が付与されますので、事前に設定しています。
1-2. Lambda を追加する
Lambda 関数を追加します。 python を使用することが多いので、今回は python の Lambda を追加します。
amplify add function? Select which capability you want to add: Lambda function (serverless function)? Provide an AWS Lambda function name: echofunction // スキーマで設定した関数名? Choose the runtime that you want to use: Python
index.py を編集します。
import jsonfrom aws_lambda_powertools.utilities.data_classes.appsync_resolver_event import (AppSyncResolverEvent,)def handler(event, context):print("received event:")event: AppSyncResolverEvent = AppSyncResolverEvent(event)print(event)return event.arguments.get('msg')
event にどのような情報が格納されているかは、aws-lambda-powertools を確認しました。
data class が提供されていたので、せっかくなので使ってみました。(使用する場合は、pipenv install aws-lambda-powertools を実行してください)
1-3. デプロイ
バックエンドをデプロイします。
amplify push --y...✔ Generated GraphQL operations successfully and saved at ../../../../src/graphqlDeployment state saved successfully.
ちなみに、ダイレクト Lambad リゾルバーではないことは、GraphQL の resolvers に Lambda を呼び出すためのリゾルバーが追加されていることから確認できます。
1-4. 動作確認
AppSync のコンソールから Query の動作確認を行います。
問題なく実行できました。
Lambda の Cloudwatch Logs も確認してみます。 実行されていますね。
2. パイプラインリゾルバー
複数の Lambda を連続的に呼び出すことができます。
また後続の Lambad は前回の Lambda の実行結果を利用することができます。
先ほど作成した Lambda の実行結果に文字列を連結する関数を試してみます。
2-1. スキーマを修正する
呼び出したい順に @function ディレクティブを定義するだけでOKです。
echofunction → concatenateEcho の順で実行されます。
type Query {echo(msg: String): String @function(name: "echofunction-${env}")concatenateEcho(msg: String): String @function(name: "echofunction-${env}") @function(name: "concatenateEcho-${env}")}
2-2. Lambda を追加する
先ほどのスキーマで定義した、concatenateEcho Lambda を追加します。
amplify add function? Select which capability you want to add: Lambda function (serverless function)? Provide an AWS Lambda function name: concatenateEcho? Choose the runtime that you want to use: PythonOnly one template found - using Hello World by default.
index.py を編集します。
import jsonfrom aws_lambda_powertools.utilities.data_classes.appsync_resolver_event import (AppSyncResolverEvent,)def handler(event, context):print("received event:")event: AppSyncResolverEvent = AppSyncResolverEvent(event)print(event)# 指定した文字列と echo 関数の結果を連結するreturn event.arguments.get('msg') + event.prev_result
Lambda が別なので、ライブラリ等は再度インストールする必要があります。 共通ライブラリなどは、Lambda Layer を追加した方が良さそうです。
2-3. デプロイ
先ほどと同じようにデプロイします。
amplify push --y...✔ Generated GraphQL operations successfully and saved at ../../../../src/graphqlDeployment state saved successfully.
2-4. 動作確認
AppSync のコンソールから Query の動作確認を行います。
文字列が連結されていますね!
3. 既存の DynanmoDB にアクセスする
以下の通り、既存の DynamoDB にアクセスしてデータを取得してみます。
3-1. スキーマを修正する
type SampleDb{id: ID!name: String!description: String!}type Query {echo(msg: String): String @function(name: "echofunction-${env}")concatenateEcho(msg: String): String @function(name: "echofunction-${env}") @function(name: "concatenateEcho-${env}")getExternalDdbItem(id: String): SampleDb @function(name: "getExternalDdbItem-${env}")}
今までと同様に、@function ディレクティブを付与したクエリを追加しました。
また、既存の テーブルに合わせた型を用意し、返り値として定義しています。
3-2. dynamodb をインポートする
以下のコマンドを実行して、DynamoDB を Storage として Amplify に取り込みます。
amplify import storage 2023-09-05 12:25? Select from one of the below mentioned services: DynamoDB table - NoSQL Database✔ Select the DynamoDB Table you want to import: · sample-ddb✅ DynamoDB Table 'sample-ddb' was successfully imported.Next steps:- This resource can now be accessed from REST APIs (`amplify add api`) and Functions (`amplify add function`)
Storage - Use an existing S3 bucket or DynamoDB table - AWS Amplify Docs
Amplify に取り込むことで、環境変数からテーブル名を取得できたり、DynamoDB にアクセスするための権限付与がCLI から実施できるようになります。
3-3. Lambda を追加する
同様にスキーマで定義した、getExternalDdbItem Lambda を追加します。
amplify add function 12.4s 2023-09-05 12:39? Select which capability you want to add: Lambda function (serverless function)? Provide an AWS Lambda function name: getExternalDdbItem? Choose the runtime that you want to use: PythonOnly one template found - using Hello World by default.Available advanced settings:- Resource access permissions- Scheduled recurring invocation- Lambda layers configuration- Environment variables configuration- Secret values configuration? Do you want to configure advanced settings? Yes? Do you want to access other resources in this project from your Lambda function? Yes # ← import した DynamoDB のアクセスするための設定? Select the categories you want this function to have access to. storage? Select the operations you want to permit on sampleddb create, read, update, deleteYou can access the following resource attributes as environment variables from your Lambda functionENVREGIONSTORAGE_SAMPLEDDB_ARNSTORAGE_SAMPLEDDB_NAMESTORAGE_SAMPLEDDB_STREAMARN? Do you want to invoke this function on a recurring schedule? No? Do you want to enable Lambda layers for this function? No? Do you want to configure environment variables for this function? No? Do you want to configure secret values this function can access? No? Do you want to edit the local lambda function now? YesEdit the file in your editor: develop/amplify-studio-sandbox/05_function_directive/amplify/backend/function/getExternalDdbItem/src/index.py? Press enter to continueSuccessfully added resource getExternalDdbItem locally.
? Do you want to access other resources in this project from your Lambda function? Yes
ここで、import した テーブルのアクセス設定を実施しています。
Lambda の権限設定(IAM Role の付与)及びテーブル情報(テーブル名、テーブルARN、ストリームARN)を環境変数として設定してくれます。
index.py を修正します。
指定した ID をもとに getItem して返す処理を実装しています。
import osimport jsonimport boto3from aws_lambda_powertools.utilities.data_classes.appsync_resolver_event import (AppSyncResolverEvent,)dynaomodb = boto3.resource("dynamodb")def handler(event, context):print("received event:")print(event)table = dynaomodb.Table(os.environ["STORAGE_SAMPLEDDB_NAME"])event: AppSyncResolverEvent = AppSyncResolverEvent(event)return table.get_item(Key={"id": event.arguments.get("id")}).get("Item")
3-4. デプロイ
同様にデプロイします。
amplify push...Deploying root stack 05functiondirective [ =================================------- ] 5/6amplify-05functiondirective-d… AWS::CloudFormation::Stack UPDATE_COMPLETE Tue Sep 05 2023 13:09:04…functiongetExternalDdbItem AWS::CloudFormation::Stack UPDATE_COMPLETE Tue Sep 05 2023 13:08:50…api05functiondirective AWS::CloudFormation::Stack UPDATE_COMPLETE Tue Sep 05 2023 13:08:39…functionconcatenateEcho AWS::CloudFormation::Stack UPDATE_COMPLETE Tue Sep 05 2023 13:08:28…functionechofunction AWS::CloudFormation::Stack UPDATE_COMPLETE Tue Sep 05 2023 13:08:28…Deployed function echofunction [ ======================================== ] 3/3Deployed function concatenateEcho [ ======================================== ] 3/3Deployed function getExternalDdbItem [ ======================================== ] 4/4LambdaFunction AWS::Lambda::Function UPDATE_COMPLETE Tue Sep 05 2023 13:08:39…Deployment state saved successfully.
3-5. 動作確認
id を指定して、クエリを実行すると、テーブルデータを取得することができました。
さいごに
今回は、@function ディレクティブの使い方について簡単に紹介しました。
@functin ディレクティブを使えば、簡単に Lambda リゾルバを実装することができました。
特に、既存の DynamoDB へのアクセスについては import した DynamoDB テーブルと問題なく連携できた(環境変数や、権限付与など)のは良い発見でした。
今後も色々試していきたいと思います。
swx-go-kawano (執筆記事の一覧)