サーバレスフレームワークでCRUDを作る

## サーバレスフレームワークでCRUDを作っていきます
使うツール

- serverless framework

サーバレスフレームワークを使うとyamlファイルで開発環境が作れるので、人的なミスを減らせてコード管理が楽になります。

使用するサービス

- iam
- lambda
- api gateway
- dynamodb

```
テーブルのスキーマ
id: String
name: String
text: String
date: Number
```


### 読み取り機能
documentClientのgetメソッドを使います

<details><summary>Dynamo.js</summary><div>

```javascript
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();

const Dynamo = {
  get: async (id, TableName) => {
    const params = {
      TableName,
      Key: { id }
    };
    const data = await documentClient
      .get(params)
      .promise();
    
    if (!data || !data.Item) {
      throw Error(`${TableName}に${id}がありません`);
    }
    
    return data.Item;
  }
};
module.exports = Dynamo;
```
</div></details>

<details><summary>getKeijibanPosts.js</summary><div>

```javascript
const Responses = require('../common/API_Responses');
const Dynamo = require('./common/Dynamo');
const TableName = process.env.tableName;

exports.handler = async (event, context, callback) => {
  console.log('event', event);

  if (!event.pathParameters || !event.pathParameters.id) {
    // idが渡されて無い
    return Responses._400({ message: 'idを指定してください' });
  }
  const id = event.pathParameters.id;
  const res = await Dynamo.get(id, TableName).catch(err => {
    console.log('error =', err);
    return null
  });

  // idが存在しない時
  if (!res) {    
    return Responses._400({ message: '指定したidのデータは存在しません' });
  }
  return Responses._200(res);
}
```
</div></details>

<details><summary>serverless.yml</summary><div>

```yml
service: kani-keijiban
frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x
  stage: test
  region: us-east-1
  environment:
    tableName: ${self:custom.tableName}
  iamRoleStatements:
    - Effect: Allow
      Action: 
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem

      Resource: 
        !GetAtt KaniKeijibanDB.Arn
custom:
  tableName: KaniPosts

functions:
  getKeijibanPosts:
    handler: lambdas/endpoints/getKeijibanPosts.handler
    events:
      - http:
          path: api/posts/{id}
          method: get
          cors: true
resources:
 Resources:
   KaniKeijibanDB:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: ${self:custom.tableName}
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

```
</div></details>


### 書き込み機能
documentClientのputメソッドを使います

serverless.ymlのfuncitonsにコードを追加

```yml
  createKeijibanPosts:
    handler: lambdas/endpoints/createKeijibanPosts.handler
    events:
      - http:
          path: api/posts
          method: post
          cors: true

```

Dynamo.jsのgetメソッドの下に追加

```javascript
put: async (item, TableName) => {
    if (!item.id) {
      throw Error('idがありません');
    }

    const params = {
      TableName,
      Item: item
    };
    
    const data = await documentClient
      .put(params)
      .promise()
    
    return data;
  }
```

createKeijibanPosts.jsを新たに作成

```javascript
const Responses = require('../common/API_Responses');
const Dynamo = require('../common/Dynamo');
const TableName = process.env.tableName;

exports.handler = async (event, context, callback) => {
  console.log('event', event);
  const item = JSON.parse(event.body);
  const res = await Dynamo.put(item, TableName).catch(err => {
    console.log('error =', err);
    return null
  });

  // 作成に失敗した時
  if (!res) {    
    return Responses._400({ message: '作成に失敗しました' });
  }
  return Responses._200(res);
}
```


これで追加機能ができました。

### 更新機能を作成

documentClientのupdateメソッドを使います

serverless.ymlのfunctionsに追加

```yaml
  updateKeijibanPosts:
    handler: lambdas/endpoints/updateKeijibanPosts.handler
    events:
      - http:
          path: api/posts/{id}
          method: put
          cors: true

```

Dynamo.jsのputメソッドの下に追加
※(`text`がlambdaの予約語と被ってしまったので`'#tx'`でプレースホルダーを使ってます)

```javascript
  update: async ({ id, tableName, updateKey, updateValue }) => {
    const params = {
      TableName: tableName,
      Key: {
        id
      },
      UpdateExpression: `set #tx = :updateValue`,
      ExpressionAttributeNames: {
        '#tx': updateKey
      },
      ExpressionAttributeValues: {
        ':updateValue': updateValue
      },
    };

    const res = await documentClient
      .update(params)
      .promise();

    return res;
  },

```

updateKeijibanPosts.jsを新たに作成

```javascript
const Dynamo = require('../common/Dynamo');
const Responses = require('../common/API_Responses');

const TableName = process.env.tableName;

exports.handler = async (event, context, callback) => {
  const id = event.pathParameters.id;

  // return the data
  const { text } = JSON.parse(event.body);
  const updateParams = {
    id,
    tableName: TableName,
    updateKey: 'text',
    updateValue: text
  };

  const res = await Dynamo.update(updateParams);
  console.log('res = ', res);
  return Responses._200({});
}

```



これで更新機能ができました。

### 削除機能

documentClientのdeleteメソッドを使います

serverless.ymlのfunctionsに追加

```yaml
  deleteKeijibanPosts:
    handler: lambdas/endpoints/deleteKeijibanPosts.handler
    events:
      - http:
          path: api/posts/{id}
          method: delete
          cors: true

```

Dynamo.jsのupdateメソッドの下に追加

```javascript
 delete: async (id, TableName) => {
    const params = {
      TableName,
      Key: { id }
    };
    const data = await documentClient
      .delete(params)
      .promise();

    return data;
  },
```

deleteKeijibanPosts.jsを新たに作成

```javascript
const Dynamo = require('../common/Dynamo');
const Responses = require('../common/API_Responses');

const TableName = process.env.tableName;

exports.handler = async (event, context, callback) => {
  const id = event.pathParameters.id;

  // return the data

  const res = await Dynamo.delete(id, TableName);
  console.log('res = ', res);
  return Responses._200(res);
}
```

### 動作確認
- 読み込み
![スクリーンショット 2020-12-13 4.29.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/4291c763-cc56-7c11-1cec-3803d17ae975.png)

- 作成
![スクリーンショット 2020-12-13 6.57.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/87054f47-2cd5-c3f7-dacc-ec79cdad013a.png)

- 編集
![スクリーンショット 2020-12-13 6.58.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/813942c7-9bb2-3405-8155-73fa1ef87a26.png)



- 削除
![スクリーンショット 2020-12-13 6.55.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528444/2a783c78-98fa-55e8-6545-5ebd3e4e5ccd.png)


まとめ
簡単なCRUD操作を知ることができた。
serverless frameworkのenvironmentに変数を書くとlambdaのprocess.envから読み込むことができる。

github: https://github.com/satouyuuki/lambda-keijiban