RDSとServerlessFrameworkでおみくじを作ろう

# はじめに
AWSの使っておみくじアプリを作るよ（そんなのRDSを使わなくてもよくね？という話はナシで）

# 環境
node.js v12.18.3

# 作ってみる

## DBインスタンスの作成
[ここ](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_GettingStarted.CreatingConnecting.MySQL.html)を参考にRDSを使ってDBインスタンスを作成していく

DBの設定

```
- データベース作成方法：簡単作成
- 設定：MySQL
- インスタンスサイズ：無料利用枠
- DBインスタンス識別子：aws-web-service
- マスターユーザー名：admin
- マスターパスワード：各自おまかせ
- パブリックアクセス可能：あり
```

作成したDBインスタンスのエンドポイントとマスターユーザーを使ってDBインスタンスにアクセス。

```console:console
mysql -h mysql-80.xxx.{リージョン}.rds.amazonaws.com -P 3306 -u admin -p
```

RDSをパブリックアクセス可能にしてるのでセキュリティ的にはゆるいけど、まあすぐ消すのでいいかなと。

## DBインスタンスにデータを追加

### DB作成
```console:console
$ create database omikuji
$ use omikuji
$ create table omikuji.kuji (id int, result varchar(3));

$ insert into kuji values (0,'大吉');
$ insert into kuji values (1,'中吉');
$ insert into kuji values (2,'小吉');

mysql> select * from kuji;
+------+--------+
| id   | result |
+------+--------+
|    0 | 大吉    |
|    1 | 中吉    |
|    2 | 小吉    |
+------+--------+

```
これで完成

## コードを書いていく
node.jsとsequelizeを使ってコマンドライン上でおみくじの結果を出力するようにする。

コマンドラインからディレクトリやファイルを作成したり、serverlessとかsequelizeをインストールしてみたりする。
ファイル構成についてだが、handler.jsにメインの処理を書いて、DBインスタンスのアクセスに必要な情報を.envに置くという構成にする。

```console:console
# ディレクトリの作成
$ mkdir omikuji

# npm初期化
$ npm init -y

# パッケージインストール
$ npm install --save serverless
$ npm install --save sequelize
$ npm install --save dotenv
$ npm install --save mysql2

# serverlessの実行環境作成
$ serverless create --template aws-nodejs 

# ファイルの作成
$ touch .env    // 環境変数用のファイル
$ touch kuji.js // sequelize モデル定義に使うファイル

# ディレクトリ構成は以下の通り
.env                    handler.js              node_modules            package.json
.gitignore              kuji.js                 package-lock.json       serverless.yml
```

とりあえず、serverlessがちゃんと動くかどうかテストしてみる。.envファイルがあることで警告は出ているが、ちゃんと動いていることが確認できたので、あとはhelloの中身をおみくじができるようにソースコードを変更してあげればよさそう。
※.envによる警告メッセージは話の本筋とは関係ないので、いったん無視していきます。

```
sls invoke local --function hello
Serverless: Running "serverless" installed locally (in service node_modules)
Serverless: Deprecation warning: Detected ".env" files. Note that Framework now supports loading variables from those files when "useDotenv: true" is set (and that will be the default from next major release)
            More Info: https://www.serverless.com/framework/docs/deprecations/#LOAD_VARIABLES_FROM_ENV_FILES
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n  \"input\": \"\"\n}"
}
```

ソースコードの修正
.envとhandler.jsを以下のようにする

```console:.env
DATABASE_NAME = "omikuji"
USERNAME = "admin"
PASSWORD = "your_db_password"
HOST =  "mysql-80.xxx.{リージョン}.rds.amazonaws.com"

```

```js:handler.js
const { Sequelize } = require('sequelize');
require('dotenv').config();

async function hello() {
  
  const sequelize = new Sequelize(
    process.env.DATABASE_NAME, 
    process.env.USERNAME, 
    process.env.PASSWORD, {
      host: process.env.HOST,
      dialect: "mysql"
  });

  try {
    await sequelize.authenticate();
    console.log('Connection has been established successfully.');
  } catch (error) {
    console.error('Unable to connect to the database:', error);
  } finally {
    sequelize.close();
  }

};

module.exports = { hello }
```

これで `sls invoke local --function hello`を実行すると以下のようになるはず。
DBの接続そのものはうまくいったみたいなので、データを取得できるようにしていく。

```console:console
sls invoke local --function hello
Serverless: Running "serverless" installed locally (in service node_modules)
Executing (default): SELECT 1+1 AS result
Connection has been established successfully.
```

## おみくじ機能を実装
おみくじするためのデータ取得処理を作成していく。
コードは以下の通り。

```js:handler.js
const { Sequelize, DataTypes } = require('sequelize');
require('dotenv').config();
const range = 3;

async function hello() {
  
  // Sequelizeインスタンスの作成
  const sequelize = new Sequelize(
    process.env.DATABASE_NAME, 
    process.env.USERNAME, 
    process.env.PASSWORD, {
      host: process.env.HOST,
      dialect: "mysql",
      logging: false,
  });

  // 0 ... range - 1 の範囲で乱数を生成
  const random = Math.floor( Math.random() * range);

  const Kuji = require("./kuji")(sequelize, DataTypes);

  try {
    const result = await Kuji.findOne({
      where: {
        id: random
      },
      attributes: ["result"],
      raw: true
    })

    return result.result;
  } catch (error) {
    console.error('Unable to connect to the database:', error);
  } finally {
    sequelize.close()
  }
};

module.exports = { hello }
```
try文でreturnを返したら後の処理ってどうなるんだろうと思ったら、きちんとfinally句は実行してくれるっぽい。（[ソース](https://qiita.com/eijenson/items/7e9e112e69b37f72353c))

```js:kuji.js
module.exports = (sequelize, DataTypes) => {
    return sequelize.define(
      "kuji",
      {
        id: {
          type: DataTypes.INTEGER,
          primaryKey: true,
        },
        result: {
          type: DataTypes.CHAR,
        },
      },
      {
        timestamps: false,
        freezeTableName: true,
      }
    );
};
```
これで実行してみると...

```console:console
# １回目
sls invoke local --function hello
Serverless: Running "serverless" installed locally (in service node_modules)
大吉

# 2回目
sls invoke local --function hello
Serverless: Running "serverless" installed locally (in service node_modules)
中吉
```

できたできた。
あとはこれをlambdaにアップしよう。

## Serverless Frameworkのserverless-layersプラグイン
lambdaにアップする前にserverless-layersプラグインを入れて、ライブラリをレイヤーにしてしまおう。

```console:console
$ npx sls plugin install --name serverless-layers
```

そしたら、レイヤーにするライブラリを格納するためのS3バケットを作成して、そこに格納するように`serverless.yml`に記述していく。あと、デプロイ時にリージョン指定するのも面倒なのであらかじめ設定しておく。

```yml:serverless.yml

service: omikuji
frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x
  region: {お使いのリージョン} # リージョン指定

functions:
  hello:
    handler: handler.hello

custom:
  serverless-layers:
    layersDeploymentBucket: hogehoge # 作成したバケット

plugins:
  - serverless-layers
```

## デプロイ

```console:console
# AWSの認証情報をcredentialsに書き込む
$ serverless config credentials --provider aws --key aws_access_key_id --secret aws_secret_access_key

# デプロイする
$ npx sls deploy 
```

これでデプロイが完了したので動かしてみる

```console:console
$ sls invoke --function hello
Serverless: Running "serverless" installed locally (in service node_modules)
"小吉"
```

できた。データベース使っておみくじ作るのは意味わからんが、ひとまずRDSとserverless frameworkを使ってちょっとしたアプリを作れたのでよしとする。