AmplifyをRubyで楽しむことはできるのか?
はじめに
最近、WebアプリをつくのにAmplifyをさわっています。バックエンド側をさくっと作りたいときには確かにいいですね。
私的に何がいいと思ったかというと:[1]
- AmplifyのCLIの質問に答えていくだけで、
APIGW
>Cognito
>Lambda
>DynamoDB
までアプリ作成に典型的に必要なものは芋づる式に作ってくれて、さらにLambdaでNodeJS
を選べばDynamoDBとAPIGW経由でCRUDする関数まで自動で生成してくれます。 - また、サービス間のIAMロールなんかも一緒に設定してくれるので権限周りも個別に設定する必要もありません
あれ? Rubyは?
ただ、「さくっと何かを作りたい時に言語としてRubyが選べるならとりあえずRubyを選んどく」くらいにはRubyistの私にとっては、Amplify CLIでRubyが選べないのはモチベーションが下がってしまいます。amplify function add
で、これをみた瞬間、.NET
に負けるなんて!とさらにへこみます。(Amplifyのバージョンは4.45.2
)
? Choose the runtime that you want to use: (Use arrow keys)
.NET Core 3.1
Go
Java
❯ NodeJS
Python
Rubyを使えるようにしてみる
では、なんとかRubyを使えるようにしてみましょう。AmplifyがRubyをサポートしていない以上は、Amplifyに気づかれないようにRubyを使うしかありません。ひとまずは、ランタイムとしてNode
を使うふりをして後でRubyに置き換える作戦です。
amplify init
Webアプリ用なので、こんな感じのinit
です。
$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project todo
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using none
? Source Directory Path: src
? Distribution Directory Path: dist
? Build Command: npm run-script build
? Start Command: npm run-script start
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html
? Please choose the profile you want to use default
amplify api add
APIから作っていきます。ここから芋づる式にDynamoDBまで作成です。ここではランタイムはNode
を使うふりをします。
$ amplify api add
? Please select from one of the below mentioned services: REST
? Provide a friendly name for your resource to be used as a label for this category in the project: todoAPI
? Provide a path (e.g., /book/{isbn}): /tasks
? Choose a Lambda source Create a new Lambda function
? Provide an AWS Lambda function name: todoFunc
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: CRUD function for DynamoDB (Integration with API Gateway)
? Choose a DynamoDB data source option Create a new DynamoDB table
Welcome to the NoSQL DynamoDB database wizard
This wizard asks you a series of questions to help determine how to set up your NoSQL database table.
? Please provide a friendly name for your resource that will be used to label this category in the project: todoDB
? Please provide table name: todoTable
You can now add columns to the table.
? What would you like to name this column: task-id
? Please choose the data type: string
? Would you like to add another column? No
Before you create the database, you must specify how items in your table are uniquely organized. You do this by specifying a primary key. The primary key uniquely identifies each item in the table so that no two items can have the same key. This can be an individual column, or a combination that includes a primary key and a sort key.
To learn more about primary keys, see:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey
? Please choose partition key for the table: task-id
? Do you want to add a sort key to your table? No
You can optionally add global secondary indexes for this table. These are useful when you run queries defined in a different column than the primary key.
To learn more about indexes, see:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.SecondaryIndexes
? Do you want to add global secondary indexes to your table? No
? Do you want to add a Lambda Trigger for your Table? No
Successfully added DynamoDb table locally
Available advanced settings:
- Resource access permissions
- Scheduled recurring invocation
- Lambda layers configuration
? Do you want to configure advanced settings? No
? Do you want to edit the local lambda function now? No
Successfully added resource todoFunc locally.
Next steps:
Check out sample function code generated in <project-dir>/amplify/backend/function/todoFunc/src
"amplify function build" builds all of your functions currently in the project
"amplify mock function <functionName>" runs your function locally
"amplify push" builds all of your local backend resources and provisions them in the cloud
"amplify publish" builds all of your local backend and front-end resources (if you added hosting category) and provisions them in the cloud
Succesfully added the Lambda function locally
? Restrict API access No
? Do you want to add another path? No
Successfully added resource todoAPI locally
Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
芋づるが長いのですが、ポイントは以下でしょうか
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: CRUD function for DynamoDB (Integration with API Gateway)
ここは後でRubyのものに置き換えるつりなので、なんでもいい気もしますが、これを選んでおくことで、Amplify側が関数にDynamoDBにアクセスするロールを付与してくれるので、こうしておくと楽です
? Restrict API access No
認証は後でも足すことができるので、ひとまず余計なところでハマらないためにもこうしておきます
amplify push
ひとまずここでpush
しておきます
$ amplify push
✔ Successfully pulled backend environment dev from the cloud.
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | ------------- | --------- | ----------------- |
| Storage | todoDB | Create | awscloudformation |
| Function | todoFunc | Create | awscloudformation |
| Api | todoAPI | Create | awscloudformation |
ランタイムをRubyに置き換える
さて、ここでこっそりランタイムとハンドラをRuby用に置き換えます。Amplifyに気づかれないように素早くおこないます。
$ aws lambda update-function-configuration --function-name todoFunc-dev --runtime ruby2.7 --handler lambda_function.lambda_handler
{
"FunctionName": "todoFunc-dev",
"FunctionArn": "arn:aws:lambda:us-east-2:578170637269:function:todoFunc-dev",
"Runtime": "ruby2.7",
"Role": "arn:aws:iam::578170637269:role/todoLambdaRolee9f33d88-dev",
"Handler": "lambda_function.lambda_handler",
"CodeSize": 11369236,
"Description": "",
"Timeout": 25,
"MemorySize": 128,
"LastModified": "2021-03-21T01:54:56.507+0000",
"CodeSha256": "3avEYotMLxKa7lXTN4P0CUPACv4ds14mjePO2IEzKms=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"ENV": "dev",
"REGION": "us-east-2"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "f89f9d07-cf82-4f68-a51e-ff2246962fe6",
"State": "Active",
"LastUpdateStatus": "Successful"
}
どうなったか
これで、FunctionはAmplifyの管理から外れましたので、今後はFunctionはAmplifyでは触らず、別で管理します
$ amplify status
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | ------------- | --------- | ----------------- |
| Storage | todoDB | No Change | awscloudformation |
| Function | todoFunc | No Change | awscloudformation | ← 別管理
| Api | todoAPI | No Change | awscloudformation |
たとえば、先に設定したハンドラ名でメソッドをつくって
require 'json'
def lambda_handler(event:, context:)
{ statusCode: 200, body: JSON.generate(event) }
end
こんなシェルでアップロードすれば動くと思います。
#!/bin/bash
FUNCTION_NAME=todoFunc-dev
if [ $# -lt 1 ]; then
echo "Usage: $0 code_file"
else
zip lambda_function.zip $1 &&
aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://lambda_function.zip
fi
$ ./update.sh lambda_function.rb
とりあえず動きました。
$ curl -s https://<api-id>.execute-api.us-east-2.amazonaws.com/dev/tasks | jq
{
"resource": "/tasks",
"path": "/tasks",
"httpMethod": "GET",
"headers": {
"Accept": "*/*",
:
:
うまくいかなかったら
{
"message": "Internal server error"
}
になったら
aws logs tail /aws/lambda/todoFunc-dev
でログを確認します。よくわからないけど、こんなハンドラが見つからない旨のエラーが出てたときがあったので
2021-03-21T02:20:35.729000+00:00 2021/03/21/[$LATEST]79ed2ec4cb494600bcb8db3e59b95c91 Critical exception from handler
2021-03-21T02:20:35.729000+00:00 2021/03/21/[$LATEST]79ed2ec4cb494600bcb8db3e59b95c91 {
"errorMessage": "undefined method `lambda_handler' for #<LambdaHandler:0x00000000020f0cb0>",
"errorType": "Function<NoMethodError>",
"stackTrace": [
]
}
以下のラムダのGUIから、Edit > Save したら動くようになりました
さて、もうここまでくると、Amplifyでやる意味あるのかと半分くらい思いますが、これでRubyで進められそうです。
-
一方で、あくまで各設定はざっくりであり、細かいところは
amplify
の外で個別に設定する必要がありそうです。 ↩︎
Discussion