【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第2章 〜API Gateway利用編〜

# 【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第2章 〜API Gateway利用編〜

## あらすじ
今年も花粉が凄いですね。自分はずっと微熱出てる状況です。

[会社にジョイン](https://www.wantedly.com/companies/temona/post_articles/41409)してから、もう２年くらい経ちました。
今回からは少し話題になっている「サーバーレスアーキテクチャ」というネタをシリーズで公開したいと思います。

※後々、リクエストに応じて更新することが多いのでストックしておくことをおすすめします。

自分は某社でCTOをしていますが、頭でっかちに理論ばっかり学習するよりは、イメージがなんとなく掴めるように学習し、実践の中で知識を深めていく方が効率的に学習出来ると考えています。

教育スタイルとしては正しい事をきっちりかっちり教えるのではなく、未経験レベルの人がなんとなく掴めるように、資料を構成していきます。

以下のようなシリーズネタで進めます。

|No.|記事|
|:-----------|-----------|
|1|[【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第1章 〜nodejsのtemplate利用編〜](http://qiita.com/devopsCoordinator/items/974a6e1b92a564f25a15)|
|2|[【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第2章 〜API Gateway利用編〜](http://qiita.com/devopsCoordinator/items/46fdce5f0356bcd35a7d)|
|3|[【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第3章 〜DynamoDB利用編〜]|

では、今回もはじめていきましょう！

## はじめに
今回は前回[【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第1章 〜nodejsのtemplate利用編〜](http://qiita.com/devopsCoordinator/items/974a6e1b92a564f25a15)の記事と同様、serverlessのnodejsのtemplateを用いてAWS APIGatewayのエンドポイントを利用するにしてみましょう。

でははじめましょう
## 環境

### Mac環境
```shell-session
10.11.6
```

### node環境

```shell-session
$ nodebrew ls

v0.12.14
```

```shell-session
$ nodebrew install-binary v7.4.0
```

```shell-session
$ nodebrew ls
v0.12.14
v7.4.0
```

### python環境

```shell-session
$ pyenv versions
  system
  2.7.12
```

```shell-session
$ pyenv install -v 3.5.2
```

```shell-session
$ eval "$(pyenv init -)"
```

```shell-session
$ pyenv shell 3.5.2
```

```shell-session
$ python --version
Python 3.5.2
```

### awscli環境
```shell-session
$ pip install awscli
```

```shell-session
$ aws --version
aws-cli/1.11.9 Python/2.7.10 Darwin/15.6.0 botocore/1.4.66
```

### serverless環境

```shell-session
$ npm install serverless -g
```

```shell-session
$ serverless --version
1.5.1
```

## プロジェクト作成

前回[【サーバーレスアーキテクチャ】をserverlessフレームワークで試す第1章 〜nodejsのtemplate利用編〜](http://qiita.com/devopsCoordinator/items/974a6e1b92a564f25a15)で作った「serverlesstest」というAWSプロファイルを引続き利用する形ですすめます。

いつも通り、serverlessコマンドでnodejsでapigatewayというプロジェクトを作って見ましょう。

```shell-session
$ serverless create --template aws-nodejs --provider aws --profile serverlesstest --path apigateway --verbose
```

**作成されたプロジェクト**

```
apigateway
├── handler.js
└── serverless.yml
```

## API Gatewayを利用する

### 設定変更

```shell-session
$ cd ~/Documents/apigateway
$ ls .
handler.js
serverless.yml
```

LambdaのステージをtestにデフォルトのテストスクリプトをAPI Gateway経由で利用してみましょう。
変更箇所は以下の通りです。
今回からはdiffの結果を表示する形で楽しようと思います。
最初はイメージわかないと思うので、最終のファイル状況も併記しておきます。



```diff:serverless.yml
--- serverless.yml.before	2017-03-06 18:26:48.000000000 +0900
+++ serverless.yml.after	2017-03-06 19:00:41.000000000 +0900
@@ -20,6 +20,9 @@
 provider:
   name: aws
   runtime: nodejs4.3
+  profile: serverlesstest
+  stage: test
+  region: ap-northeast-1

 # you can overwrite defaults here
 #  stage: dev
@@ -56,7 +59,12 @@
 functions:
   hello:
     handler: handler.hello
-
+    events:
+      - http:
+          path: hello
+          method: get
+          cors: true
+
 #    The following are a few example events you can configure
 #    NOTE: Please make sure to change your handler code to work with those events
 #    Check the event documentation for details
```

**変更後のファイル(変更箇所だけを抜粋)**

```txt:serverless.yml
provider:
  name: aws
  runtime: nodejs4.3
  profile: serverlesstest
  stage: test
  region: ap-northeast-1

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
          cors: true
```

### デプロイ
修正が完了したら、デプロイをしてみましょう。

```shell-session
$ serverless deploy --provider aws --profile serverlesstest -v
```

デプロイが上手く行くと以下のようなインフォメーションが表示されるので
endpointsという箇所のURLをコピーしましょう。

#### API GateWayの情報を確認する。

```
Service Information
service: apigateway
stage: test
region: ap-northeast-1
api keys:
  None
endpoints:
  GET - https://xxxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/test/hello
functions:
  apigateway-test-hello: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxx:function:apigateway-test-hello

Stack Outputs
HelloLambdaFunctionArn: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxx:function:apigateway-test-hello
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxx:function:apigateway-test-hello:1
ServiceEndpoint: https://xxxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/test
ServerlessDeploymentBucketName: apigateway-test-xxxxxxxxxxxxxx
```

### ブラウザでアクセスしてみる

さきほどコピーしたエンドポイントのURLにブラウザでアクセスしてみましょう
以下の画像のように表示されれば成功です。

![ブラウザでAPIGateWayのエンドポイントにアクセスした結果](https://qiita-image-store.s3.amazonaws.com/0/74153/64909820-490c-d489-584c-b76acac71ed9.png)

## 少しフライングしてDynamoDBにもアクセスしてみる

次回にDynamoDBにアクセスするRestfulなAPIを作る予定でしたが
少しフライングして、この回でもDynamoDBにアクセスして結果を読み取るようにしてみましょう。
いつも通り解説はほどほどに。

serverless.ymlを使って

* Lambdaで実行するユーザーにDynamoDBの周りの権限を付与(iamRoleStatementsらへん)
* resourcesでDynamoDBの設定も同時に設定

を設定しています。


### serverless.ymlを変更する

```diff:serverless.yml
--- serverless.yml.before	2017-03-06 19:00:41.000000000 +0900
+++ serverless.yml.after	2017-03-07 10:52:00.000000000 +0900
@@ -23,6 +23,17 @@
   profile: serverlesstest
   stage: test
   region: ap-northeast-1
+  iamRoleStatements:
+  - Effect: Allow
+    Action:
+      - dynamodb:DescribeTable
+      - dynamodb:Query
+      - dynamodb:Scan
+      - dynamodb:GetItem
+      - dynamodb:PutItem
+      - dynamodb:UpdateItem
+      - dynamodb:DeleteItem
+    Resource: "arn:aws:dynamodb:ap-northeast-1:*:*"

 # you can overwrite defaults here
 #  stage: dev
@@ -57,14 +68,14 @@
 #    - exclude-me-dir/**

 functions:
-  hello:
-    handler: handler.hello
+  readAll:
+    handler: handler.readAll
     events:
       - http:
-          path: hello
+          path: members
           method: get
           cors: true
-
+
 #    The following are a few example events you can configure
 #    NOTE: Please make sure to change your handler code to work with those events
 #    Check the event documentation for details
@@ -95,3 +106,21 @@
 #     NewOutput:
 #       Description: "Description for the output"
 #       Value: "Some output value"
+resources:
+  Resources:
+    MembersDynamoDbTable:
+      Type: 'AWS::DynamoDB::Table'
+      DeletionPolicy: Retain
+      Properties:
+        AttributeDefinitions:
+          -
+            AttributeName: id
+            AttributeType: S
+        KeySchema:
+          -
+            AttributeName: id
+            KeyType: HASH
+        ProvisionedThroughput:
+          ReadCapacityUnits: 1
+          WriteCapacityUnits: 1
+        TableName: 'members'
```

### handler.jsを変更する

次の章でDynamoDBを使ったRestfulなAPIを作ってみようと考えていましたが
たいしたレベルでもないので、フライングして今回の章でもDynamoDBにアクセスして
結果を取得するようなAPIを作ってみましょう。

```diff:handler.js

--- handler.js.before	2017-03-06 14:47:50.000000000 +0900
+++ handler.js.after	2017-03-07 10:29:24.000000000 +0900
@@ -1,16 +1,17 @@
 'use strict';

-module.exports.hello = (event, context, callback) => {
-  const response = {
-    statusCode: 200,
-    body: JSON.stringify({
-      message: 'Go Serverless v1.0! Your function executed successfully!',
-      input: event,
-    }),
-  };
+const membersReadAll = require('./members-read-all.js');

-  callback(null, response);
+module.exports.readAll = (event, context, callback) => {
+  membersReadAll(event, (error, result) => {
+    const response = {
+      statusCode: 200,
+      headers: {
+        "Access-Control-Allow-Origin" : "*"
+      },
+      body: JSON.stringify(result),
+    };

-  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
-  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
+    context.succeed(response);
+  });
 };
```

### members-read-all.jsを作成する

nodejs用のaws-sdkのライブラリを使ってアクセスするスクリプトを作ってみましょう
単純にDynamoDBのmembersというテーブルに対してscanをした結果を取得するだけの
スクリプトです。

```txt:members-read-all.js
'use strict';

const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();

module.exports = (event, callback) => {
  const params = {
    TableName: 'members',
  };

  return dynamoDb.scan(params, (error, data) => {
    if (error) {
      callback(error);
    }
    callback(error, data.Items);
  });
};

```

### ライブラリをインストールする

nodejsで使うライブラリをインストールしましょう。
今回はpackage.jsonなども使わずインストールしてます。

```shell-session
$ npm install aws-sdk --save
```

これでプロジェクトフォルダの配下にnode_modulesが作成されて対象のライブラリが保存されました。

### デプロイ

長かったですね。実際にデプロイしてみましょう。

```shell-session
$ serverless deploy --provider aws --profile serverlesstest -v
```

デプロイ結果から、エンドポイントをコピーしブラウザからもアクセスしてみましょう。
https://xxxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/test/members

「[]」という結果が表示されましたか?

### AWSコンソールからDynamoDBを確認する
![DynamoDB](https://qiita-image-store.s3.amazonaws.com/0/74153/cb1f6998-7683-e0de-a130-a5975f788f39.png)

#### DynamoDBにデータをいれる
データが空な事を確認するというのもつまらないので
AWSコンソール画面から手動でデータを登録してみましょう。

##### DynamoDBのテーブルに項目を作成
![DynamoDBのテーブルに項目を作成する](https://qiita-image-store.s3.amazonaws.com/0/74153/0823442a-1aa0-4698-a7a0-ed5020baef31.png)

##### DynamoDBのテーブルにデータを登録
今回はidとnameで登録してみましょう。
![DynamoDBのテーブルにデータを登録する](https://qiita-image-store.s3.amazonaws.com/0/74153/bc67d10c-1b6c-a5a9-bff7-4d9030c258e4.png)


## API Gateway経由でDynamoDBの結果を確認する

再度ブラウザで
https://xxxxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/test/members
にアクセスして、さきほど登録したDynamoDBの値が取得出来るか確認してみましょう。

![API Gatewayの結果](https://qiita-image-store.s3.amazonaws.com/0/74153/2edab0dc-1171-6f5f-70cc-c2d99fb40a0f.png)


# 編集後記
少しフライングしてDynamoDBまで連携させてみました。APIの結果を表示するところまでしかネタに含めなかったのでイマイチ実感がわかないと思いますが、余力のある人はAPIを利用する側も作ってみてください。

次回は、もう少しCRUDをRestfulな感じで対応出来るAPIを作ってみようと思います。
お楽しみに。