CloudFormationを使用してexpressアプリをlambda functionとしてデプロイ、API gatewayを通して公開する
express.jsでアプリを作る
まず、ふつうにアプリを書く。ファイル名はapp.jsとする
// app.js
const express = require('express');
const cors = require('cors');
const app = express();
const port = 3000;
app.use(cors());
app.get('/', (req, res) => {
console.log(req.method, req.url);
res.send({ status: true });
});
app.listen(port, () => {
console.log(`listening on http://localhost:${port}`);
});
GETで何かが返ってくるごくごく簡単なアプリ
aws-serverless-expressの導入
middlewareを使うのでpackageを導入
$ npm install --save aws-serverless-express
サンプルのファイルを拝借
$ git clone https://github.com/awslabs/aws-serverless-express.git ase
$ cp -r ase/example/scripts .
$ cp ase/example/api-gateway-event.json .
$ cp ase/example/cloudformation.yaml .
$ cp ase/example/lambda.js .
$ cp ase/example/simple-proxy-api.yaml .
$ rm -rf ase
サンプルのpackage.jsonからスクリプトなどを拝借
package.json
"scripts": {
"start": "node app.local.js",
"config": "node ./scripts/configure.js",
"deconfig": "node ./scripts/deconfigure.js",
"local": "node scripts/local",
"invoke-lambda": "aws lambda invoke --function-name $npm_package_config_functionName --region $npm_package_config_region --payload file://api-gateway-event.json lambda-invoke-response.json && cat lambda-invoke-response.json",
"create-bucket": "aws s3 mb s3://$npm_package_config_s3BucketName --region $npm_package_config_region",
"delete-bucket": "aws s3 rb s3://$npm_package_config_s3BucketName --region $npm_package_config_region",
"package": "aws cloudformation package --template ./cloudformation.yaml --s3-bucket $npm_package_config_s3BucketName --output-template packaged-sam.yaml --region $npm_package_config_region",
"deploy": "aws cloudformation deploy --template-file packaged-sam.yaml --stack-name $npm_package_config_cloudFormationStackName --capabilities CAPABILITY_IAM --region $npm_package_config_region",
"package-deploy": "npm run package && npm run deploy",
"delete-stack": "aws cloudformation delete-stack --stack-name $npm_package_config_cloudFormationStackName --region $npm_package_config_region",
"setup": "npm install && (aws s3api get-bucket-location --bucket $npm_package_config_s3BucketName --region $npm_package_config_region || npm run create-bucket) && npm run package-deploy"
},
"config": {
"s3BucketName": "YOUR_UNIQUE_BUCKET_NAME",
"region": "YOUR_AWS_REGION",
"cloudFormationStackName": "YOUR_STACK_NAME",
"functionName": "YOUR_SERVERLESS_EXPRESS_LAMBDA_FUNCTION_NAME",
"accountId": "YOUR_ACCOUNT_ID",
"profile": "YOUR_PROFILE"
}
configの修正
- cloudFormationStackNameはわかりやすい名前
- profileはaws-cliの使いたいprofile
これ以外のconfig項目は設定スクリプトで変更する
package.json
"cloudFormationStackName": "YOUR_STACK_NAME",
"profile": "YOUR_PROFILE"
nodejsのランタイムが古いので修正
cloudformation.yaml#L60
- Runtime: nodejs8.10
+ Runtime: nodejs12.x
APIのtitleをわかりやすいものに変更
simple-proxy-api.yaml#L4
- title: AwsServerlessExpressApi
+ title: <YourApiTitle>
設定変更スクリプトの実行
package.jsonに設定を書き換えるスクリプトが用意されているので実行
$ npm run config -- --account-id="<accountId>" --bucket-name="<bucketName>" --region="<region>"
- accountId→AWSのアカウントID
- bucketName→S3のバケットの名前(わかりやすい名前をつける)
- region→ap-northeast-1とか
--function-name
オプションもあるが、指定するとかえってハマるので私は指定しなかった
アプリの修正
app.jsの修正
- aws-serverless-expressのmiddlewareをexpress appに食わせる
- app.listenは別ファイルに移動するため削除
- appをmoduleとしてexportしておく
app.js
+ const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');
- const port = 3000;
...
+ app.use(awsServerlessExpressMiddleware.eventContext());
...
- app.listen(port, () => {
- console.log('Listening on port ' + port + ' ...');
- });
...
+ module.exports = app;
app.local.jsを追加(ローカル実行用)
修正したapp.jsのmoduleをimportしてlisten実行部分をもってくる
app.local.js
const app = require('./app');
const port = 3000;
app.listen(port, () => {
console.log(`listening on http://localhost:${port}`);
});
デプロイ実行
$ npm run setup
問題なければ CloudFormation にスタックが追加される。スタックの詳細の「出力」タブから Lambda function、API gateway、エンドポイントにアクセスできる
必要ならば設定情報を破棄
$ npm run deconfig
その他
- デプロイに失敗した場合はAWS Console の CloudFormation > スタック > イベント を見てエラー内容などを確認する
- エラーで生成に失敗したスタックは都度削除したほうがよさそう