【NestJS初心者】管理画面API作ったよって話② 〜TypeOrm データベース作成して、接続するぞ！〜

[前回][1]は、Nestjsのプロジェクトをダウンロードして、環境構築まで完了しました。
今回は、TypeOrmを使用して、データベースの作成〜接続までを行っていこうと思います！
[1]: [https://qiita.com/peach_s/items/a440fbd12120f2d822f4]

※[Nest.js公式サイト（データベース編）](https://docs.nestjs.com/techniques/database)
※[typeormの公式サイトはこちら][typeorm]
[typeorm]:[https://typeorm.io/#/]

##環境
Node.js v16.10.0
MySQL v8.0.28
TypeOrm
macOS


## まずは、Mysql起動
　`$ mysql.server start`を使用して、mysqlを起動

```json
$ mysql.server start
Starting MySQL
 SUCCESS! 
```

#### MySQLログイン
`mysql -u root `を使用してMySQLにログイン

```
$ mysql -u root 

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.28 Homebrew

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
```

※もし、`Permission denied`のエラーが出る場合は、

```
$ sudo mysql -u root 
```
を使って実行が必要です。


#### userとpasswordを作成します。

```
mysql> CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';
```

##### 順調！順調！

#### データベースを作成します。
今回は、`admin_info`という名前で、データベースを作成する。

```
mysql> create database admin_info;
Query OK, 1 row affected (0.01 sec)
```

```
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| admin_info         |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)
```
できてる〜〜〜✨

ここまでできたら、プロジェクトのアプリケーションの方でTypeOrmの設定に入ります！


## TypeOrmの設定

#### TypeOrmのパッケージ等を追加

```
$ yarn add @nestjs/typeorm
$ yarn add typeorm
$ yarn add mysql
```

####TypeOrmModuleの設定を`app.module.ts`に追記する

```typescript
//app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AdminController } from './admin/admin.controller';
import { AdminService } from './admin/admin.service';
@Module({
  imports: [
　　　//ここから
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'admin_info',
      entities: [],
      autoLoadEntities: true,
      synchronize: false,
    }),
　　//ここまで
  ],
  controllers: [AppController, AdminController],
  providers: [AppService, AdminService],
})
export class AppModule {}
```
 一回試しに`$ yarn run start`で起動させてみる！


```typescript
[Nest] 86006  - 2022/02/04 23:38:06     LOG [NestFactory] Starting Nest application...
[Nest] 86006  - 2022/02/04 23:38:06     LOG [InstanceLoader] TypeOrmModule dependencies initialized +95ms
[Nest] 86006  - 2022/02/04 23:38:06     LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 86006  - 2022/02/04 23:38:06   ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)...
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
    at Handshake.Sequence._packetToError 
    at Handshake.ErrorPacket 
    at Protocol._parsePacket 
    at Parser._parsePacket 
    at Parser.write
    at Protocol.write
    at Socket.<anonymous> 
    at Socket.<anonymous> 
    at Socket.emit (node:events:390:28)

.....
[Nest] 85968  - 2022/02/04 23:38:13   ERROR [ExceptionHandler] ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
.
.
.


```
### エラー出た_:(´ཀ`」 ∠):

`ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client` .....
クライアントが、サーバから要求された認証プロトコルをサポートしていない.....
え( ＾∀＾)？

ググる。

#### 原因・解決策
MySQLでパスワードの認証形式が変更されているが、
Node.jsの「mysql」にはまだ実装されていないことが原因らしかった、、、

認証方法を`mysql_native_password`に変更することで、この問題は解決できました！
下記のコマンドをmysqlで実行！

```
mysql> ALTER USER 'user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'
```


```typescript
$ yarn run start

[Nest] 86713  - 2022/02/04 23:59:39     LOG [NestFactory] Starting Nest application...
[Nest] 86713  - 2022/02/04 23:59:39     LOG [InstanceLoader] TypeOrmModule dependencies initialized +96ms
[Nest] 86713  - 2022/02/04 23:59:39     LOG [InstanceLoader] AppModule dependencies initialized +1ms
[Nest] 86713  - 2022/02/04 23:59:39     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +9ms
[Nest] 86713  - 2022/02/04 23:59:39     LOG [RoutesResolver] AppController {/}: +11ms
[Nest] 86713  - 2022/02/04 23:59:39     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 86713  - 2022/02/04 23:59:39     LOG [NestApplication] Nest application successfully started +2ms
```

動いたぁぁぁ💓


MySQLとの接続は完了したようなので、
テーブル作成に移ります！

## テーブル作成

テーブルを作成するにあたり、
- entityファイル
- migrationファイル（自動生成されます）
- ormconfigファイル


の3つを新規作成していきます。

まずは、
src配下に`typeorm`のフォルダを作成し、さらに`entity`と`migration`フォルダを追加。
`entity`フォルダの中に`admin_info.entity.ts`ファイルを作成。
アプリケーション直下にorm.configファイルを作成。

```
.
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   ├── main.ts
│   └── typeorm←これ追加する
│       ├── entity←これ追加する
│       └── migration←これ追加する
├── ormconfig.ts←これ追加する
```


#### ①Entityを作成していきます。

今回は、このようなテーブルを作成していこうと思います！
テーブル名：`author_info`

|カラム名|データ型|メモ|
|--|--| --|
|Id|number|ID|
|user|string|ユーザー名|
|status|number|ステータス|
|imagePhotoPath|string|写真|
|description|string|説明|
|createdAt|Date|作成日|
|updatedAt|Date|アップデート日|

`admin_info.entity.ts`ファイルの中に
作成したいカラムを下記のように書いていきます。

```typescript
import {
    Entity,
    Column,
    PrimaryGeneratedColumn,
    CreateDateColumn,
    UpdateDateColumn,
    BaseEntity,
  } from 'typeorm';

  @Entity({ name: 'admin_info' })
  export class AdminInfo extends BaseEntity {
    @PrimaryGeneratedColumn()
    id!: number;
    @Column({ name: 'user', unique: true })
    user!: string;
    @Column({ name: 'status' })
    status!: number;
    @Column({ name: 'image_photo_path' })
    imagePhotoPath!: string;
    @Column({ name: 'description', default: 0 })
    description!: string;
    @CreateDateColumn({ name: 'created_at' })
    createdAt!: Date;
    @UpdateDateColumn({ name: 'updated_at' })
    updatedAt: Date;
  }
```

#### ②ormconfig.tsの設定
その次に、`ormconfig.ts`に下記のように設定を記載。

```typeorm
module.exports = {
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'user',
    password: 'password',
    database: 'admin_info',
    entities: ['dist/src/typeorm/entity/**/*.js'],
    migrations: ['dist/src/typeorm/migration/**/*.js'],
    subscribers: ['dist/src/typeorm/subscriber/**/*.js'],
    synchronize: false,
    logging: false,
    cli: {
      migrationsDir: 'src/typeorm/migration',
    },
  };
```

```typescript
// アプリケーションを再起動。
$ yarn run start

yarn run v1.22.11
$ nest start
[Nest] 87256  - 2022/02/05 0:20:11     LOG [NestFactory] Starting Nest application...
[Nest] 87256  - 2022/02/05 0:20:11     LOG [InstanceLoader] TypeOrmModule dependencies initialized +63ms
[Nest] 87256  - 2022/02/05 0:20:11     LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 87256  - 2022/02/05 0:20:11     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +15ms
[Nest] 87256  - 2022/02/05 0:20:11     LOG [RoutesResolver] AppController {/}: +6ms
[Nest] 87256  - 2022/02/05 0:20:11     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 87256  - 2022/02/05 0:20:11     LOG [NestApplication] Nest application successfully started +2ms
```

#### ③マイグレーションスクリプトの生成
最後に、マイグレーションスクリプトの生成します！
今回は自動生成しようと思うので、

```
$ yarn ts-node node_modules/.bin/typeorm migration:generate -n (マイグレーション名）
```

を実行。
※今回はマイグレーション名をinitとしました。

```typescript
Migration /path/your/app/admin-app/src/typeorm/migration/1643988029988-init.ts has been generated successfully.
✨  Done in 2.09s.
```
先ほど作った`/typeorm/migrationファイル`の中にマイグレーションファイルが自動生成されました！

やった〜！


```typescript
///typeorm/migrationファイルの中身
import {MigrationInterface, QueryRunner} from "typeorm";

export class init1643988029988 implements MigrationInterface {
    name = 'init1643988029988'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE \`admin_info\` (\`id\` int NOT NULL AUTO_INCREMENT, \`user\` varchar(255) NOT NULL, \`status\` int NOT NULL, \`image_photo_path\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL DEFAULT '0', \`created_at\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX \`IDX_83114dcb0b3c33b8018f811d4f\` (\`user\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP INDEX \`IDX_83114dcb0b3c33b8018f811d4f\` ON \`admin_info\``);
        await queryRunner.query(`DROP TABLE \`admin_info\``);
    }

}

```

#### 出来てる( ´∀｀)出来てる( ´∀｀)


### マイグレーションの実行
```
$ yarn ts-node node_modules/.bin/typeorm migration:run
```

```typescript
query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'admin_info' AND `TABLE_NAME` = 'migrations'
query: CREATE TABLE `migrations` (`id` int NOT NULL AUTO_INCREMENT, `timestamp` bigint NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB
query: SELECT * FROM `admin_info`.`migrations` `migrations` ORDER BY `id` DESC
0 migrations are already loaded in the database.
1 migrations were found in the source code.
1 migrations are new migrations that needs to be executed.
query: START TRANSACTION
query: CREATE TABLE `admin_info` (`id` int NOT NULL AUTO_INCREMENT, `user` varchar(255) NOT NULL, `status` int NOT NULL, `image_photo_path` varchar(255) NOT NULL, `description` varchar(255) NOT NULL DEFAULT '0', `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `IDX_83114dcb0b3c33b8018f811d4f` (`user`), PRIMARY KEY (`id`)) ENGINE=InnoDB
query: INSERT INTO `admin_info`.`migrations`(`timestamp`, `name`) VALUES (?, ?) -- PARAMETERS: [1643988029988,"init1643988029988"]
Migration init1643988029988 has been executed successfully.
query: COMMIT
✨  Done in 2.15s.
```
```
mysql> show columns from admin_info;
+------------------+--------------+------+-----+----------------------+--------------------------------------------------+
| Field            | Type         | Null | Key | Default              | Extra                                            |
+------------------+--------------+------+-----+----------------------+--------------------------------------------------+
| id               | int          | NO   | PRI | NULL                 | auto_increment                                   |
| user             | varchar(255) | NO   | UNI | NULL                 |                                                  |
| status           | int          | NO   |     | NULL                 |                                                  |
| image_photo_path | varchar(255) | NO   |     | NULL                 |                                                  |
| description      | varchar(255) | NO   |     | 0                    |                                                  |
| created_at       | datetime(6)  | NO   |     | CURRENT_TIMESTAMP(6) | DEFAULT_GENERATED                                |
| updated_at       | datetime(6)  | NO   |     | CURRENT_TIMESTAMP(6) | DEFAULT_GENERATED on update CURRENT_TIMESTAMP(6) |
+------------------+--------------+------+-----+----------------------+--------------------------------------------------+
```
### 無事にテーブルが作成されました！✨✨

もし、上手く作成されない場合は、
migrationファイルやentityファイルの読み込みが上手くいっていないかもしれません。
ormconfigの中でentitiesやmigrationsをdistファイルから読み込むように指定しています。
そのため一度、distファイルを削除して（`yarn run start`した際に再度作成されるので、心配無用です！）
もう一度`yarn run start`でアプリケーションを起動してください！

その際に、distファイルの中にも`entity`や`migration`が作成されているか確認を忘れずにd(￣ ￣)



なお、 マイグレーションの状態確認やリバーとは下記のコマンドでできますよ( ＾∀＾)

```
// マイグレーションの状態確認
yarn ts-node node_modules/.bin/typeorm migration:show
```

```
// マイグレーションのrevert
yarn ts-node node_modules/.bin/typeorm migration:revert
```


### tree
今回修正した場所を明記しています

```
※一部抜粋
admin-app
├── README.md
├── dist
│   ├── ormconfig.d.ts
│   ├── ormconfig.js
│   ├── ormconfig.js.map
│   ├── src
│   │   └── typeorm←これ追加されてる（自動で）
│   │       ├── entity←これ追加されてる（自動で）
│   │       │   ├── admin_info.entity.d.ts
│   │       │   ├── admin_info.entity.js
│   │       │   └── admin_info.entity.js.map
│   │       └── migration←これ追加されてる（自動で）
│   │           ├── 1643988029988-init.d.ts
│   │           ├── 1643988029988-init.js
│   │           └── 1643988029988-init.js.map
│   └── tsconfig.build.tsbuildinfo
├── nest-cli.json
├── node_modules
├── ormconfig.ts←これ追加する
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts←この中にtypeorm設定追記する
│   ├── app.service.ts
│   ├── main.ts
│   └── typeorm←これ追加する
│       ├── entity←これ追加する
│       │   └── admin_info.entity.ts←これ追加する
│       └── migration←これ追加する
│           └── 1643988029988-init.ts←これ追加されてる（自動で）
├── test
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
```


p.s.
思ったよりも、スムーズに作成できた。
でも、Nest.jsの公式サイト見ると、他にも設定方法などがありそう。
もっと簡単な方法やスタイリッシュな書き方わかる人、ご教授いただきたいです。。。m(_ _)m
それにしても、Nest.jsのサイト英語ばっかりで、読むのつらみ、、、


. . .
次は、controllerやserviceの実装に入っていくっ！