LoopBackのTodoListチュートリアルを実施

## 概要
Node.jsのフレームワークであるLoopBackのTodoListを作成するチュートリアルを実施する。
参考：https://loopback.io/doc/en/lb4/Getting-started.html#create-a-new-project

上記のチュートリアルでは、DBにIn-MemoryDBを利用しているが、ここではMySQLを利用する。

## 環境構築
以下を利用してDockerで環境を構築する。
https://github.com/Esfahan/docker-loopback-tutorial

# TODO Tutorial
todo-list tutorialを行う。
参考：https://loopback.io/doc/en/lb4/todo-tutorial.html

## Requirements
- Node.js at v10 or greater


## アプリケーションを作成
Scaffoldをする。
参考：http://loopback.io/doc/en/lb4/todo-tutorial-scaffolding.html

```shell-session
[root@882e7183f062 code]# lb4 app
? Project name: todo-list
? Project description: A todo list API made with LoopBack 4.
? Project root directory: todo-list
? Application class name: TodoListApplication
? Select features to enable in the project Enable eslint, Enable prettier, Enable mocha, Enable loopbackBuild, Enable vscode, Enable docker, Enable repositories, Enable services

# omitted

added 623 packages from 807 contributors and audited 5182 packages in 14.122s
found 0 vulnerabilities


Application todo-list was created in todo-list.

Next steps:

$ cd todo-list
$ npm start
```

## Mysql用のDataSourceを作成
チュートリアルではIn-MemoryDBを利用するが、ここではMySQLを利用する。

- 参考
    - https://loopback.io/doc/en/lb2/MySQL-connector.html
    - https://loopback.io/doc/ja/lb3/Connecting-to-MySQL.html

### MySQL用のパッケージをインストール

```shell-session
$ npm install loopback-connector-mysql --save
```

### MySQL用のDataSourceを作成

```shell-session
$ lb4 datasource
? Datasource name: db
# 方向キーでスクロールしてMySQLを選択
? Select the connector for db: MySQL (supported by StrongLoop)
# そのままEnter
? Connection String url to override other settings (eg: mysql://user:pass@host/db):
? host: loopback-db
? port: 3306
? user: loopback
? password: [hidden]
? database: loopback
   create src/datasources/db.datasource.config.json
   create src/datasources/db.datasource.ts
   update src/datasources/index.ts

Datasource Db was created in src/datasources/
```

Mysqlの接続情報は以下に作成される。
src/datasources/db.datasource.config.json

```json:src/datasources/db.datasource.config.json
{
  "name": "db",
  "connector": "mysql",
  "url": "",
  "host": "loopback-db",
  "port": 3306,
  "user": "loopback",
  "password": "xxxxxxxxxx",
  "database": "loopback"
}
```

## Modelを作成
参考：http://loopback.io/doc/en/lb4/todo-tutorial-model.html

```shell-session
lb4 model
? Model class name: todo
# Entityを選択
? Please select the model base class Entity (A persisted model with an ID)
? Allow additional (free-form) properties? No
Model Todo will be created in src/models/todo.model.ts

Let's add a property to Todo
Enter an empty property name when done

? Enter the property name: id
? Property type: number
? Is id the ID property? Yes
? Is id generated automatically? Yes

#? Is id generated automatically? No
#? Is it required?: Yes
# そのままEnter
? Default value [leave blank for none]:

Let's add another property to Todo
Enter an empty property name when done

? Enter the property name: title
? Property type: string
? Is it required?: Yes
# そのままEnter
? Default value [leave blank for none]:

Let's add another property to Todo
Enter an empty property name when done

? Enter the property name: desc
? Property type: string
? Is it required?: No
# そのままEnter
? Default value [leave blank for none]:

Let's add another property to Todo
Enter an empty property name when done

? Enter the property name: isComplete
? Property type: boolean
? Is it required?: No
# そのままEnter
? Default value [leave blank for none]:

Let's add another property to Todo
Enter an empty property name when done

# そのままEnter(空でEnterで終了する)
? Enter the property name:
   create src/models/todo.model.ts
   update src/models/index.ts

Model Todo was created in src/models/
```

## Migrationを実行してDBにTableを作成
Migrationを実行する。Modelファイルの中のTable定義を参照してTableが生成される。
今回の場合は、src/models/todo.model.tsの以下の部分が参照される。

```ts:src/models/todo.model.ts
export class Todo extends Entity {
  @property({
    type: 'number',
    id: true,
    generated: true,
  })
  id?: number;

  @property({
    type: 'string',
    required: true,
  })
  title: string;

  @property({
    type: 'string',
  })
  desc?: string;

  @property({
    type: 'boolean',
  })
  isComplete?: boolean;
```

### Migrationを実行

```
$ npm run-script build
$ npm run migrate
```

既存のテーブルを全て削除してmigrationする場合は以下。

```
$ npm run migrate -- --rebuild
```

### 作成されるTable

```sql
mysql> show create table loopback.Todo;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                         |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Todo  | CREATE TABLE `Todo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(512) NOT NULL,
  `desc` varchar(512) DEFAULT NULL,
  `isComplete` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
```

## Repositoryを作成
参考：https://loopback.io/doc/en/lb4/todo-tutorial-repository.html

```shell-session
$ lb4 repository
# そのままEnter
? Please select the datasource DbDatasource

# スペースを押して選択する
? Select the model(s) you want to generate a repository
❯◉ Todo

# そのままEnter
? Please select the repository base class DefaultCrudRepository (Legacy juggler bridge)
   create src/repositories/todo.repository.ts
   update src/repositories/index.ts

Repository TodoRepository was created in src/repositories/
```

## Controllerを作成
参考：https://loopback.io/doc/en/lb4/todo-tutorial-controller.html

```shell-session
lb4 controller
? Controller class name: todo
Controller Todo will be created in src/controllers/todo.controller.ts

# 2つ目を選択
? What kind of controller would you like to generate?
  Empty Controller
❯ REST Controller with CRUD functions
# Todoを選択
? What is the name of the model to use with this CRUD repository? Todo
# TodoRepositoryを選択
? What is the name of your CRUD repository? TodoRepository
? What is the name of ID property? id
? What is the type of your ID? number
? Is the id omitted when creating a new instance? Yes
# そのままEnter
? What is the base HTTP path name of the CRUD operations? /todos
    force src/controllers/todo.controller.ts
   update src/controllers/index.ts

Controller Todo was created in src/controllers/
```

## ブラウザからアクセスして動作を確認する
参考：https://loopback.io/doc/en/lb4/todo-tutorial-putting-it-together.html

### loopbackを起動

```shell-session
$ npm start

> todo-list@1.0.0 prestart /code/todo-list
> npm run build


> todo-list@1.0.0 build /code/todo-list
> lb-tsc


> todo-list@1.0.0 start /code/todo-list
> node -r source-map-support/register .

Server is running at http://[::1]:3000
Try http://[::1]:3000/ping
```

### ブラウザからアクセス
http://localhost:3000

<img width="1435" alt="Screen Shot 2020-04-13 at 17.06.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105936/feda03c1-bfb9-8ce8-b88b-edd6e77a1f22.png">

### API経由でデータを追加する

```shell-session
$ curl -H 'Content-Type:application/json' \
  -d '{ "title": "get the milk" }' \
  http://localhost:3000/todos
{"id":1,"title":"get the milk"}

$ curl -H 'Content-Type:application/json' \
  -d '{ "title": "forget the milk " }' \
  http://localhost:3000/todos
{"id":2,"title":"forget the milk "}
```

#### DBを確認

```sql
mysql> select * from loopback.Todo;
+----+------------------+------+------------+
| id | title            | desc | isComplete |
+----+------------------+------+------------+
|  1 | get the milk     | NULL |       NULL |
|  2 | forget the milk  | NULL |       NULL |
+----+------------------+------+------------+
2 rows in set (0.00 sec)
```

#### Todoリストを確認
http://localhost:3000/todos

```json:Response
[
  {
    "id": 1,
    "title": "get the milk",
    "desc": null,
    "isComplete": null
  },
  {
    "id": 2,
    "title": "forget the milk ",
    "desc": null,
    "isComplete": null
  }
]
```

### データを更新

```
$ curl --request PATCH -H 'Content-Type:application/json' \
  -d '{ "desc": "need milk for cereal." }' \
  http://localhost:3000/todos/1
```

#### Todoリストを確認
http://localhost:3000/todos

id:1のdescがUpdateされた。

```json:Response
[
  {
    "id": 1,
    "title": "get the milk",
    "desc": "need milk for cereal.",
    "isComplete": null
  },
  {
    "id": 2,
    "title": "forget the milk ",
    "desc": null,
    "isComplete": null
  }
]
```

### データを削除

```
$ curl --request DELETE http://localhost:3000/todos/1
```

#### Todoリストを確認
http://localhost:3000/todos

id:1が削除された。

```json:Response
[
  {
    "id": 2,
    "title": "forget the milk ",
    "desc": null,
    "isComplete": null
  }
]
```

以上