ServerlessFrameworkで作る初めてのサーバーレスアプリケーション

初投稿になります。
都内不動産Web系企業にて新卒として入社して３ヶ月ほどが経過しました。

SalesForceやAWSなど、クラウド系のツールをよく使っています。

サーバーレスフレームワークを最近業務上ではない部分で個人的に勉強をしているので、投稿していきたいと思います。

#ServerlessFrameworkとは

直訳気味に説明すると下記になります。

サーバーレスフレームワークとは、イベント駆動かつ、実行ごとに課金される仕組みで、オートスケーリングなfunctionをビルド、デプロイを支援してくれるCLIツールです。

簡単にいうなら、AWSなどのクラウドプロバイダを使ってより手軽にサーバーレスアプリケーションを作ることのできるツールになっています。本来ならGUIでポチポチと設定しなければいけないLambdaやAPI Gateway、DynamoDBなどの設定をコードで記入できるのも魅力になっています。

#ServerlessFrameworkのインストール

```bash:install
npm install -g serverless
```
# 基本コマンド
```bash:basic
serverless create --template aws-nodejs --path productName
// サーバーレスアプリの作成
serverless deploy -v
// デプロイ
serverless deploy function -f hello
//functionを指定したデプロイ
```
上記を見ていただけばわかるように、とても簡単にサーバーレスなアプリケーションを作成、デプロイまでできてしまいます。

# サンプルアプリの作成
それでは実際に作成に移っていきましょう。上記で紹介したコマンドでアプリケーションを作成します。

```bash:install
 sls create -t aws-nodejs -path sls-sample
```

では早速dynamoDBのテーブルの作成を行います。ymlファイルの記述を行います。
sls createで作成されたymlファイルの編集を行います。

```yaml:serverless.yml
service: sls-sample

plugins:
  - serverless-offline

custom:
  allowedHeaders:
    - Accept
    - Content-Type
    - Content-Length
    - Authorization
    - X-Amz-Date
    - X-Api-Key
    - X-Amz-Security-Token
    - X-Amz-User-Agent
    - app_user_id
    - app_user_name

provider:
  name: aws
  runtime: nodejs10.x
  region: ap-northeast-1
  stage: prod
  memorySize: 128
  timeout: 5
  endpointType: regional
  environment:
   ARTICLES_TABLE: ${self:service}-${opt:stage, self:provider.stage}
  iamRoleStatements:
    - Effect: Allow
      Action: 
        - dynamodb:Query
        - dynamodb:PutItem
      Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.ARTICLES_TABLE}"

functions:
  add-article:
    handler: api/add-article.handler
    description: POST /article
    events:
      - http:
          path: article
          method: post
          cors:
            origin: '*'
            headers: ${self:custom.allowedHeaders}

resources:
  Resources:
    ArticlesTable:
      Type: AWS::DynamoDB::Table
      DeletionPolicy: Retain
      Properties:
        TableName: ${self:provider.environment.ARTICLES_TABLE}
        AttributeDefinitions:
          - AttributeName: user_id
            AttributeType: S
          - AttributeName: timestamp
            AttributeType: N
          - AttributeName: article_id
            AttributeType: S
        KeySchema:
          - AttributeName: user_id
            KeyType: HASH
          - AttributeName: timestamp
            KeyType: RANGE
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        GlobalSecondaryIndexes:
          - IndexName: article_id-index
            KeySchema:
              - AttributeName: article_id
                KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
```

記述内容は上記のようになります。ymlファイルは複雑なように見えますが、一つ一つの単語の意味が明確なので、設定をコードでやっているだけで、実際のファイル作成とあまり難しさは変わりません。

それに、コードを見れば一発でどんな設定を行ったかがわかるので、かなり便利だと思います。

```bash:basic

serverless deploy -v

//functionを指定したデプロイ
```
それではデプロイをしてAWSにログインし、dynamoDBを見てみてください。
実際に新しいテーブルが作られているはずです。

# POST機能
```javascript:add-article.js
/**
 * Route: POST /article
 */

const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1'});

const moment = require('moment');
const uuidv4 = require('uuid/v4');

const dynamodb = new AWS.DynamoDB.DocumentClient();
const tableName = process.env.ARTICLES_TABLE;

exports.handler = async (event) => {
    try {
        let item = JSON.parse(event.body).Item;
        item.user_id = event.headers.app_user_id;
        item.user_name = event.headers.app_user_name;
        item.article_id = item.user_id + ':' + uuidv4()
        item.timestamp = moment().unix();
        item.expires = moment().add(90, 'days').unix();

        let data = await dynamodb.put({
            TableName: tableName,
            Item: item
        }).promise();

        return {
            statusCode: 200,
            headers: {
                'Access-Control-Allow-Origin': '*'
            },
            body: JSON.stringify(item)
        };
    } catch (err) {
        console.log("Error", err);
        return {
            statusCode: err.statusCode ? err.statusCode : 500,
            headers:{
                'Access-Control-Allow-Origin': '*'
            },
            body: JSON.stringify({
                error: err.name ? err.name : "Exception",
                message: err.message ? err.message : "Unknown error"
            })
        };
    }
}
```

では実際にこのサーバーレスフレームワークをオフラインで用いることのできるツールである「serverless-offline」を用いてテストをしてみます。

```bash
npm i --save serverless-offline
```

このコマンドでlocalhost:3000でオフラインでアプリケーションが動くようになります。
Postmanなどを使ってPOSTリクエストを叩いてみてください！　
URLは下記になります。
localhost:3000/article