AWS SAM CLIでTypeScriptがネイティブサポートされました (パブリックプレビュー)
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
します。
{ "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)でした!