この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Lambda関数をTypeScriptで書きたい
こんにちは、のんピ(@non____97)です。
皆さんはLambda関数を簡単にTypeScriptで書きたいと思ったことはありますか? 私はあります。
AWS CDKを使うようになってからTypeScriptを触るようになり、型がない世界に戻れなくなりました。そのままAWS CDKだけでなく、Lambda関数もTypeScriptで書きたいと思うようになるのは当然の流れです。しかし、2022/2/28時点でLambda関数はTypeScriptをネイティブサポートしていません。
Lambda関数にTypeScirptで書かれたコードをデプロイするためには、一度トランスコンパイルをしてあげるというワンステップが必要です。
このワンステップが私にとってとてつもなく面倒です。
楽をするのであれば、以下記事のようにwebpackやAWS CDKのaws-lambda-nodejsを使えばトランスコンパイルとパッケージのバンドルを一気に行えます。
しかし、面倒くさがりな私はもう少し簡単にTypeScriptで書かれたコードをLambda関数にデプロイできればと思っていました。
そんなある時、ブログネタに困って What’s Newを眺めていたら、見つけてしまいました。
「 Announcing TypeScript native support in the AWS Serverless Application Model (AWS SAM) CLI (using esbuild) (public preview) 」 を
つまりは何?
2022/2/28時点でパブリックプレビューですが、AWS SAM CLIでTypeScriptがネイティブサポートされました。
動きとしては、esbuildを使ってTypeScirptで書かれたコードのトランスコンパイルとパッケージのバンドルを行ってくれます。
AWS公式ブログを確認したところ、トランスコンパイル用のコマンドは不要で、今まで通りsam build
、sam deploy
をするだけでLambda関数のデプロイができそうでした。
やってみた
早速、TypeScriptで書かれたコードをAWS SAMを使ってLambda関数にデプロイしてみます。
AWS SAM CLIのバージョンは1.40.0
で、Node.jsのバージョンは14.19.0
です。
> sam --version
SAM CLI, version 1.40.0
> node --version
v14.19.0
sam init
を実行して、TypeScriptのAWSクイックスタートテンプレートでプロジェクトを初期化します。
> sam init
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Infrastructure event management
8 - Machine Learning
Template: 1
Use the most popular runtime and package type? (Python and zip) [y/N]: N
Which runtime would you like to use?
1 - dotnet5.0
2 - dotnetcore3.1
3 - go1.x
4 - java11
5 - java8.al2
6 - java8
7 - nodejs14.x
8 - nodejs12.x
9 - python3.9
10 - python3.8
11 - python3.7
12 - python3.6
13 - ruby2.7
Runtime: 7
What package type would you like to use?
1 - Zip
2 - Image
Package type: 1
Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.
Select your starter template
1 - Hello World Example
2 - Hello World Example TypeScript
Template: 2
Project name [sam-app]:
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: nodejs14.x
Architectures: x86_64
Dependency Manager: npm
Application Template: hello-world-typescript
Output Directory: .
Next steps can be found in the README file at ./sam-app/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sam-app && sam pipeline init --bootstrap
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
sam init
後のディレクトリは以下の通りです。
> tree
.
├── .gitignore
├── README.md
├── events
│ └── event.json
├── hello-world
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .npmignore
│ ├── .prettierrc.js
│ ├── app.ts
│ ├── jest.config.ts
│ ├── package.json
│ ├── tests
│ │ └── unit
│ │ └── test-handler.test.ts
│ └── tsconfig.json
└── template.yaml
4 directories, 13 files
package.json
を確認すると、ts-nodeやesbuildはもちろん、eslintやprettierなどがdevDependencies
に指定されていました。hello-world
ディレクトリ配下で忘れずにnpm install
します。
package.json
{
"name": "hello_world",
"version": "1.0.0",
"description": "hello world sample for NodeJS",
"main": "app.js",
"repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
"author": "SAM CLI",
"license": "MIT",
"dependencies": {
},
"scripts": {
"unit": "jest",
"lint": "eslint '*.ts' --quiet --fix",
"compile": "tsc",
"test": "npm run compile && npm run unit"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.92",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.13",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"esbuild": "^0.14.14",
"esbuild-jest": "^0.5.0",
"eslint": "^8.8.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.5.0",
"prettier": "^2.5.1",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
}
}
また、./hello-world/app.ts
を確認すると、TypeScriptで書かれたLambdaハンドラーが定義されていました。
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
/**
*
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
* @param {Object} event - API Gateway Lambda Proxy Input Format
*
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
* @returns {Object} object - API Gateway Lambda Proxy Output Format
*
*/
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
let response: APIGatewayProxyResult;
try {
response = {
statusCode: 200,
body: JSON.stringify({
message: 'hello world',
}),
};
} catch (err) {
console.log(err);
response = {
statusCode: 500,
body: JSON.stringify({
message: 'some error happened',
}),
};
}
return response;
};
続いて、template.yaml
を確認すると、メタデータとしてesbuildのプロパティが設定されていました。ターゲットはes2020
です。Node Greenを確認したところ、今回使用しているNode.js v14.19.0は概ねサポートされていそうです。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: get
Metadata: # Manage esbuild properties
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
それでは、
「いざ sam build
」
という気持ちは山々ですが、現在パブリックプレビュー中であるので、TypeScriptを使うために以下のいずれかの下準備が必要です。
- 環境変数
SAM_CLI_BETA_ESBUILD
に1
を設定する - 以下のパラメータを
samconfig.toml
に追加する
[default.build.parameters]
beta_features = true
[default.sync.parameters]
beta_features = true
sam build
、sam sync
を実行する際に--beta-features
を付ける- プロンプトでベータ版の機能の使用について表示されたら、
y
を選択する
今回は一番簡単な4つ目の方法を使用します。
sam build
を実行すると、ベータ版の機能を有効化するか聞かれました。
> sam build
Your template contains a resource with logical ID "ServerlessRestApi", which is a reserved logical ID in AWS SAM. It could result in unexpected behaviors and is not recommended.
Using esbuild for bundling Node.js and TypeScript is a beta feature.
Please confirm if you would like to proceed with using esbuild to build your function.
You can also enable this beta feature with 'sam build --beta-features'. [y/N]:
もちろん有効化するのでy
を選択します。しばらく待つと、正常にビルドが完了しました。
You can also enable this beta feature with 'sam build --beta-features'. [y/N]: y
Experimental features are enabled for this session.
Visit the docs page to learn more about the AWS Beta terms https://aws.amazon.com/service-terms/.
Building codeuri: /<ディレクトリ名>/sam-app/hello-world runtime: nodejs14.x metadata: {'BuildMethod': 'esbuild', 'BuildProperties': {'Minify': True, 'Target': 'es2020', 'Sourcemap': True, 'EntryPoints': ['app.ts']}} architecture: x86_64 functions: ['HelloWorldFunction']
Running NodejsNpmEsbuildBuilder:CopySource
Running NodejsNpmEsbuildBuilder:NpmCI
Running NodejsNpmEsbuildBuilder:EsbuildBundle
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
[*] Deploy: sam deploy --guided
ビルドアーティファクトの./.aws-sam/build
配下を確認すると、template.yaml
とトランスコンパイルされたJavaScriptファイルが出力されていました。
> ls ./.aws-sam/build/
HelloWorldFunction template.yaml
> ls ./.aws-sam/build/HelloWorldFunction/
app.js app.js.map
それでは次にデプロイです。
sam deploy --guided
でガイドを出してデプロイします。今回は全てデフォルトで進めます。
> sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]:
AWS Region [us-east-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]:
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]:
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]:
HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]:
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
Looking for resources needed for deployment:
Enter MFA code for arn:aws:iam::<AWSアカウントID>:mfa/<IAMユーザー名>:
Creating the required resources...
Successfully created!
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1quh29sgv0k4t
A different default S3 bucket can be set in samconfig.toml
Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
Learn more about samconfig.toml syntax at
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Uploading to sam-app/4915c8283ad33a06ffcffbf42832df65 1353 / 1353 (100.00%)
Deploying with following values
===============================
Stack name : sam-app
Region : us-east-1
Confirm changeset : False
Disable rollback : False
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-1quh29sgv0k4t
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
Uploading to sam-app/571e71e4a3a21168e8bc6cb1f26393e7.template 1336 / 1336 (100.00%)
Waiting for changeset to be created..
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
---------------------------------------------------------------------------------------------------------------------------------
+ Add HelloWorldFunctionHelloWorldPe AWS::Lambda::Permission N/A
rmissionProd
+ Add HelloWorldFunctionRole AWS::IAM::Role N/A
+ Add HelloWorldFunction AWS::Lambda::Function N/A
+ Add ServerlessRestApiDeployment47f AWS::ApiGateway::Deployment N/A
c2d5f9d
+ Add ServerlessRestApiProdStage AWS::ApiGateway::Stage N/A
+ Add ServerlessRestApi AWS::ApiGateway::RestApi N/A
---------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:us-east-1:<AWSアカウントID>:changeSet/samcli-deploy1646050506/195d1b69-2364-42b1-8c72-bf30752d0c17
2022-02-28 21:15:19 - Waiting for stack create/update to complete
CloudFormation events from stack operations
---------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole -
CREATE_IN_PROGRESS AWS::IAM::Role HelloWorldFunctionRole Resource creation Initiated
CREATE_COMPLETE AWS::IAM::Role HelloWorldFunctionRole -
CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction -
CREATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunction Resource creation Initiated
CREATE_COMPLETE AWS::Lambda::Function HelloWorldFunction -
CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi -
CREATE_IN_PROGRESS AWS::ApiGateway::RestApi ServerlessRestApi Resource creation Initiated
CREATE_COMPLETE AWS::ApiGateway::RestApi ServerlessRestApi -
CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldPe -
rmissionProd
CREATE_IN_PROGRESS AWS::Lambda::Permission HelloWorldFunctionHelloWorldPe Resource creation Initiated
rmissionProd
CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47f -
c2d5f9d
CREATE_IN_PROGRESS AWS::ApiGateway::Deployment ServerlessRestApiDeployment47f Resource creation Initiated
c2d5f9d
CREATE_COMPLETE AWS::ApiGateway::Deployment ServerlessRestApiDeployment47f -
c2d5f9d
CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage -
CREATE_COMPLETE AWS::ApiGateway::Stage ServerlessRestApiProdStage -
CREATE_IN_PROGRESS AWS::ApiGateway::Stage ServerlessRestApiProdStage Resource creation Initiated
CREATE_COMPLETE AWS::Lambda::Permission HelloWorldFunctionHelloWorldPe -
rmissionProd
CREATE_COMPLETE AWS::CloudFormation::Stack sam-app -
---------------------------------------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
-----------------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------------
Key HelloWorldFunctionIamRole
Description Implicit IAM Role created for Hello World function
Value arn:aws:iam::<AWSアカウントID>:role/sam-app-HelloWorldFunctionRole-15LVMB2PC87I1
Key HelloWorldApi
Description API Gateway endpoint URL for Prod stage for Hello World function
Value https://hh5nw1npm7.execute-api.us-east-1.amazonaws.com/Prod/hello/
Key HelloWorldFunction
Description Hello World Lambda Function ARN
Value arn:aws:lambda:us-east-1:<AWSアカウントID>:function:sam-app-HelloWorldFunction-Lgi9nNpOqPbY
-----------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - sam-app in us-east-1
デプロイが完了した後、キーがHelloWorldApi
のURLにアクセスすると、ステータスコード200
で{"message":"hello world"}
が返ってきました。
> curl -L https://hh5nw1npm7.execute-api.us-east-1.amazonaws.com/Prod/hello/ -w '\n%{http_code}' -s
{"message":"hello world"}
200
最後に後片付けとしてsam delete
で作成されたリソースを削除します。
> sam delete
Are you sure you want to delete the stack sam-app in the region us-east-1 ? [y/N]: y
Are you sure you want to delete the folder sam-app in S3 which contains the artifacts? [y/N]: y
- Deleting S3 object with key sam-app/4915c8283ad33a06ffcffbf42832df65
- Deleting S3 object with key sam-app/571e71e4a3a21168e8bc6cb1f26393e7.template
- Deleting Cloudformation stack sam-app
Deleted successfully
早くGAして欲しい
パブリックプレビューですが、AWS SAM CLIでTypeScriptがネイティブサポートされたことを紹介しました。
型のある生活に入り、抜け出せなくなってしまった私にとっては非常に嬉しいアップデートです。GAされることを楽しみに待っています。
ちなみに、AWS SAM AccelerateもTypeScriptをベータサポートしているので、コードの更新があったら自動でデプロイするといったことも可能です。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!