[AWS] Serverless FrameworkでRESTfulAPIを作成する

AWSでRESTfulAPIをAPIGateway+Lambdaで作ろうとすると、ブラウザ上でGUIをいじくるわけですが操作感が独特で慣れるまでちょっとばかし辛いものがあります。そんな時に活躍してくれるのが「Serverless Framework」。YAMLをチョロっと書くだけでそのあたりの設定を良い感じに行ってくれます。

今回はドキュメントの内容に沿って、Serverless Frameworkの導入から実行までを行います。

初期設定

インストール

Serverless FrameworkはNode.js上で動作するため、Node.jsの6以降をインストールしておきます。 https://nodejs.org/ja/nodejs.org

Serverless Frameworkその物のインストールはコマンド一発です。npmはNode.jsと同時に自動的に入ります。

$ npm install -g serverless

今回は以下のバージョンがインストールされました。

$ serverless --version                        
Framework Core: 1.67.3
Plugin: 3.6.6
SDK: 2.3.0
Components: 2.29.3

IAMユーザーの作成

AWSのマネジメントコンソールにログインし、Serverlessフレームワークで利用するIAMユーザーを作成、アクセスキーとシークレットをメモしておきます。ドキュメントによるとこのときの権限はAdministratorAccessが必要とのこと。ちょっと怖いのだがw

IAMのアクセスキーとシークレットを環境変数にセットします。以下のコマンドを毎回入力するか、zshなら~/.zshrcbashなら~/.bashrcなどに書いておきます。

export AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx
export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

ファイルに書いた場合は再読み込みを忘れずに。

$ source ~/.zshrc

なおAWS CLIの初期設定がすでに終わっている場合は、こちらの情報が自動的に使われますので上記の設定は不要です。 blog.katsubemakito.net

Serverlessアカウントを作成しログイン

このステップは省略しても構いません。一応Serverlessのサイトに登録しておくとWebブラウザ上から様々な操作が可能になります。serverlessのサイトでアカウントを作成し、CLI上でログインを行います。

その後、以下のコマンドを打つと自動的にWebブラウザが起動するのでログインします。

$ serverless login

CLIに戻り以下のように表示されていれば成功です。

Serverless: You sucessfully logged in to Serverless.
Serverless: Please run 'serverless' to configure your service

サービスを作成する

プロジェクトの作成

新しくプロジェクトを作成するか、プロバイダや言語は何にするか、プロジェクトの名前を聞かれますのでそれぞれ入力します。

$ serverless
Serverless: No project detected. Do you want to create a new one? Yes
Serverless: What do you want to make? AWS Node.js
Serverless: What do you want to call this project? hello

Project successfully created in 'hello' folder.

Webブラウザから色々できるServerlessサイトの機能を利用するか聞かれます。必要がなければNoでスキップしてください。

Serverless: Would you like to enable this? No
You can run the “serverless” command again if you change your mind later.

最初の1回だけタブキーを押すと自動補完が行われる設定を行うか聞かれます。これはやっておいた方が楽です。シェルの種類はご自分の環境に合わせて選択してください。

Serverless: Would you like to setup a command line <tab> completion? Yes
Serverless: Which Shell do you use ? zsh
Serverless: We will install completion to ~/.zshrc, is it ok ? Yes

最終的に新しくフォルダが作成され、中に以下の2つのファイルが生成されていれば成功です。

$ ls hello   
handler.js      serverless.yml

serverless.yml

このファイルでプロジェクトの様々な設定を管理します。実際の中身をコメント行と空行をスキップしてのぞいてみます。

$ cat serverless.yml | grep -v '^#' | grep -v '^\s*$' 

以下の通りです。今回はHTTP経由でLambdaを実行させたいのでeventsから下を追加(コメントアウト)しています。またここには記載されていませんがデフォルトだとリージョンはus-east-1、ステージはdevに上がります。

service: hello
provider:
  name: aws
  runtime: nodejs12.x
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

handler.js

実際に実行されるLambda関数です。今回はシンプルな結果にして様子を見たいのでinput: event,の行はまるごと削除しておきます。

'use strict';

module.exports.hello = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Go Serverless v1.0! Your function executed successfully!',
        input: event,
      },
      null,
      2
    ),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

ローカルでテスト

serverless invoke localでローカル上でLambda関数を実行できます。AWSへアップする前の確認に利用できますね。

$ serverless invoke local --function hello
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\"\n}"
}

なお、localを付けないとAWSへすでに上がっているLambda関数を実行してくれます。

デプロイ

現在の内容で実際にAWSへアップします。これはコマンド一発。ファイルに修正をして再度上げたい場合も同様です。

$ serverless deploy

色々とメッセージが表示されますが、この中のendpointsの部分をメモしておきます。

endpoints:
  GET - https://q7t3eobpgb.execute-api.us-east-1.amazonaws.com/dev/hello

実行する

先ほどのendpointsのURLにブラウザで直接アクセスするか、

curlコマンドを実行してみます。

$ curl https://q7t3eobpgb.execute-api.us-east-1.amazonaws.com/dev/hello
{
  "message": "Go Serverless v1.0! Your function executed successfully!"
}

handler.jsの内容が実行されているのがわかりますね。

2つ目の関数を作成する

関数を追加したくなった際、もう一つプロジェクトを作っても良いのですが、serverless.ymlに追記することでも実現できます。

handler2.js

新しく「handler2.js」というファイル名で次のLambda関数を用意しました。

'use strict';

module.exports.hello2 = async event => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'I am handler2.js',
      },
      null,
      2
    ),
  };
};

sererless.yml

sererless.ymlのファイル後半に先ほど追加したLambda関数の情報を追記しました。

service: hello
provider:
  name: aws
  runtime: nodejs12.x
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
  hello2:
    handler: handler2.hello2
    events:
      - http:
          path: hello2
          method: get

handler2.hello2」の部分は言い換えると「(ファイル名).(関数名)」を意味しています。今回はファイルを2つに分けましたが、最初に作成された「handler.js」内に複数の関数を定義する方法でも問題なく動作します。

実行する

デプロイすると今まで1つだったendpointsも2つ出てきましたね。

$ serverless deploy
(中略)
endpoints:
  GET - https://oqa3rh8s7l.execute-api.us-east-1.amazonaws.com/dev/hello
  GET - https://oqa3rh8s7l.execute-api.us-east-1.amazonaws.com/dev/hello2

実行してみるとそれぞれ無事に動いているのが確認できました。

$ curl https://oqa3rh8s7l.execute-api.us-east-1.amazonaws.com/dev/hello
{
  "message": "Go Serverless v1.0! Your function executed successfully!"
}

$ curl https://oqa3rh8s7l.execute-api.us-east-1.amazonaws.com/dev/hello2
{
  "message": "I am handler2.js"
}

AWS上から削除する

使わなくなったサービスをAWS上から削除するのもコマンド一発です。

$ serverless remove

コマンド実行後にcurlで取得すると403が返却されるようになり、

$ curl -i https://q7t3eobpgb.execute-api.us-east-1.amazonaws.com/dev/hello
HTTP/2 403 
content-type: application/json
content-length: 23
date: Tue, 21 Apr 2020 10:37:38 GMT

{"message":"Forbidden"}

さらにもうしばらく待つとドメイン毎、設定から消えていることが確認できます。

$ curl -i https://q7t3eobpgb.execute-api.us-east-1.amazonaws.com/dev/hello 
curl: (6) Could not resolve host: q7t3eobpgb.execute-api.us-east-1.amazonaws.com

もう一度同じ内容で作成したい場合、AWSから削除されただけでローカルのファイルはそっくりそのまま残っていますのでserverless deployするだけです。ただしドメインは別の物になるのでご注意を。

設定

ステージを指定

sererless.yml内のproviderの下でstageを指定できます。以下でqaに設定しています。デフォルトはdev。

service: hello
provider:
  name: aws
  runtime: nodejs12.x
  stage: qa
#(以下略)

なお実行時に一時的に変更したいだけの場合は、以下のようにオプションとして渡すことでYAMLの指定を上書きすることが可能です。例えば完成品を本番にデプロイしたいといった場合にはこちらを利用する感じでしょうか。

$ serverless deploy --stage qa

リージョンを指定

sererless.yml内のproviderの下でregionを指定できます。以下で東京に設定しています。デフォルトはus-east-1。当然と言えば当然なんですがリージョン変えるとURLも変わりますのでご注意くださいませ。

service: hello
provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
#(以下略)

こちらも実行時に指定することでYAMLの設定を上書きすることができます。以下はオレゴンを指定しています。

$ serverless deploy --region us-west-2

独自ドメインを設定する

blog.katsubemakito.net

もっと使いこなす

クエリーを受け取る

GETでのリクエスト時に/(stage)/hello?foo=123などURLのお尻にくっついているクエリーを取得したり、POSTでリクエストされた場合にリクエストボディの値を取得します。 blog.katsubemakito.net

外部サーバと通信する

外部サーバからHTTPで通信するサンプルです。特に制約は無いので普通にリクエストすればOKです。 blog.katsubemakito.net

DynamoDBと連携する

受け取ったデータをDynamoDBに保存するサンプルです。ServerlessFrameworkのおかげでテーブルの作成やIAMの設定もYAMLで大半が行えちゃいます。 blog.katsubemakito.net

S3と連携する

RESTful API経由でS3を操作します。保存や削除、バケット内のオブジェクトの一覧を取得するシンプルなサンプルです。 blog.katsubemakito.net

おまけ

既視感ヤバいなと思って調べたら2017年にも同じことやってることが判明するなどw

参考ページ