AWS

Expressで作ったAPIをaws-serverless-expressでサーバレス化した話

More than 1 year has passed since last update.


はじめに

最近Expressで作ったAPIを、aws-serverless-expressを使ってサーバレス化したときの手順をまとめる。

今回はExpressをサーバレス化する話なので、Expressを使ったAPIの構築については触れない。


前提


  • Expressで既にAPIを作っている

  • AWSアカウントを持っている

  • aws-cliが設定済みである


aws-serverless-expressとは

awslabが出してる、Expressで作ったRESTful APIをサーバレス化するツール。

サーバレスAPIは、Express + Lambda + API Gatewayを用いて構築する。

このツールの凄いところは、アプリケーションでいじるところが殆ど無いところだ。

なので、既にExpressでAPIサーバを構築している人なら、少しの手順を踏むだけで、サーバレスなAPIを簡単に構築することができる。

細かいことはGithubのREADMEに書いてある。

天下のAWSさんが出しているだけあって、exampleもちゃんとしてる。

1からAPIを作る人はexampleをコピーするところから始めると良いと思う。

Githubのリンクはこちら


サーバレス化


1. aws-serverless-expressのインストール

以下を実行して、aws-serverless-expressをインストール。

$ npm install --save aws-serverless-express


2. exampleのpackage.jsonのscriptsをコピー

aws-serverless-expressのexampleには、既に便利なscriptが用意されているため、今回はそれを利用する。

自分のプロジェクトのpackage.jsonに以下をコピペする。

(Windows以外の人はwin-config以下はコピペする必要なし)。

  "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",
"win-config": "npm run config",
"win-deconfig": "npm run deconfig",
"win-local": "npm run local",
"win-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",
"win-create-bucket": "aws s3 mb s3://%npm_package_config_s3BucketName% --region %npm_package_config_region%",
"win-delete-bucket": "aws s3 rb s3://%npm_package_config_s3BucketName% --region %npm_package_config_region%",
"win-package": "aws cloudformation package --template ./cloudformation.yaml --s3-bucket %npm_package_config_s3BucketName% --output-template packaged-sam.yaml --region %npm_package_config_region%",
"win-deploy": "aws cloudformation deploy --template-file packaged-sam.yaml --stack-name %npm_package_config_cloudFormationStackName% --capabilities CAPABILITY_IAM --region %npm_package_config_region%",
"win-package-deploy": "npm run win-package && npm run win-deploy",
"win-delete-stack": "aws cloudformation delete-stack --stack-name %npm_package_config_cloudFormationStackName% --region %npm_package_config_region%",
"win-setup": "npm install && (aws s3api get-bucket-location --bucket %npm_package_config_s3BucketName% --region %npm_package_config_region% || npm run win-create-bucket) && npm run win-package-deploy"
}

次に、exampleで使用しているscriptファイルや、yamlファイルをコピーする必要がある。

プロジェクトルートで以下を実行。

$ 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


3. awsアカウントとs3バケットの設定

先程コピーしたスクリプトを使用して、awsアカウントとs3バケットの設定を行う。

$ npm run config -- \

--account-id="<accountId>" \
--bucket-name="<bucketName>" \
--region="<region>" \ # optinal
--function-name="<functionName>" # optinal

このコマンドの実行が成功すると、以下の3つのファイルが書き換わる。


  • package.json

  • simple-proxy-api.yaml

  • cloudformation.yaml


4. app.jsの修正

Expressをaws-serverless-expressで動かすには少しコードに手を加えなくてはならない。

まず、app.jsでaws-serverless-expressのMiddlewareを有効化する必要がある。

以下をapp.jsに追加

const express = require('express')

+ const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')

const app = express()

+ app.use(awsServerlessExpressMiddleware.eventContext())

次に、aws-serverless-expressではUNIXドメインソケットで別の箇所でlistenしているらしいので、app.jsに書いてるlistenメソッドは削除する必要がある。

- app.listen()

最後に、lambda.jsでapp.jsを読み込む必要があるため、app.jsをmodule化する必要がある。

以下の一文をapp.jsの末尾に追加

+ module.exports = app


5. lambda.jsの修正

exampleからコピーした、lambda.jsを修正する。

先程修正したapp.jsのパスを指定して、importするだけで良い。

const awsServerlessExpress = require('aws-serverless-express')

- const app = require('./app')
+ const app = require('./path/to/app')


6. デプロイ

exampleで用意されているsetupスクリプトを実行するだけで良い。

$ npm run setup

setupスクリプトを実行すると以下が順番に実行される。


  1. npm install

  2. デプロイ先のs3バケットの作成

  3. CloudFormationのテンプレートの作成

  4. CloudFormationのデプロイ

3. で生成されるCloudFormationのテンプレートはpackaged-sam.yamlというファイルに出力される。

packaged-sam.yamlの内容は以下の通り


  1. API Gatewayの構築

  2. Lambda Functionの作成

  3. Lambdaの実行権限のあるIAMロールの作成

setupスクリプトの実行が成功していれば、CloudFormationのStackが実行されているはずなので、AWSコンソールにログインして、CloudFormationが実行されているか確認する。


7. 確認

AWSコンソールにサインインし、API Gatewayサービスを選択。

新しくAPIが作成されていることを確認する。

新しく作成されたAPIを選択し、「ステージ > prod > URLの呼び出し」に書かれているURLをBase URLとしてAPIを叩いてみる。

Expressで予め作成したAPIを実行して、動くことを確認する。

以下は例。

$ curl http://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/v1/fuga


まとめ

Expressを使って既に動いているAPIを、たった7つのステップで、簡単にサーバレス化できてしまった。

これからサーバレス、マイクロサービスがトレンドになる中、簡単に既存のAPIをサーバレス化できてしまう仕組みは本当に凄いと思った。流石AWSさん。

API Gatewayの作成まではできたので、後は独自ドメインにしたりhttpsを有効化したり、色々カスタマイズすると良いと思う。

自分はめんどくさがって手動でポチポチ設定してしまったが、本当はこの辺もCloudFormationでできるはずなので、暇になったら設定してみる。