NestJS+TypeORMでDB接続して動作確認まで

基本的に自分用メモです．

## DBの準備

今回はmysqlを使います．

予め適当なデータベースとユーザを作成して権限付与までしておきます．テーブルはまだ作らなくてOKです．

~~~mysql
CREATE DATABASE testdb;
CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL ON testdb.* TO 'testuser'@'localhost';
~~~

## NestJSのプロジェクト作成

NestJSのプロジェクトを作成して簡単な動作確認まで行います．

細かいところは公式ドキュメントを参照: https://docs.nestjs.com

~~~bash
npm i -g @nestjs/cli
nest new nest-typeorm-handson // とりあえずnpm選択して進めます
nest g module user
nest g controller user
nest g service user
~~~

これでnest-typeorm-handson以下にこんな感じにファイルが生成されます．

~~~ bash
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── main.ts
└── user
    ├── user.controller.spec.ts
    ├── user.controller.ts
    └── user.module.ts
~~~

## 最低限の動作の追加

UserServiceに以下を追加します

~~~typescript:user.service.ts
sample(): string {
  return 'UserService';
}
~~~

UserControllerに以下のコンストラクタとメソッドを追加します．

~~~typescript:user.controller.ts
constructor(private readonly service: UserService) {}
@Get()
get() {
  return this.service.sample();
}
~~~

`npm run start`で起動し，ブラウザで`localhost:3000/user`にアクセスするとUserServiceの文字が出るはずです．

これで準備完了です．

## TypeORMを使ってDB接続する

ここからTypeORMの出番です．

まずは準備します．

`npm install --save @nestjs/typeorm typeorm mysql`

そしてAppModuleの@Moduleの中身のimportsを以下のように書き換えます

~~~typescript:app.module.ts
imports: [
  UserModule, // UserModuleはもともと追加されています
  TypeOrmModule.forRoot({
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'testuser',
    password: 'password',
    database: 'testdb',
    entities: [],
    synchronize: false,
  })
],
~~~

さて，ここで`npm run start`します．

すると以下のようなエラーが出る場合があります

~~~bash
[Nest] 70420   - 2020-09-11 22:23:00   [TypeOrmModule] Unable to connect to the database. Retrying (1)... +8ms
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
    at Handshake.Sequence._packetToError (/Users/home/nest-typeorm-handson/node_modules/mysql/lib/protocol/sequences/Sequence.js:47:14)
~~~

これはユーザの設定関連が原因なのでmysqlで以下を実行すると治ります．

~~~mysql
ALTER USER 'testuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
~~~

次にEntityを作ります．src/user/user.entity.tsを作成し，以下のように作ります．

~~~typescript:user.entity.ts
import { Entity, PrimaryColumn, Column } from "typeorm";

@Entity()
export class User {
    @PrimaryColumn({ length: 255 })
    id: string;

    @Column({ length: 255 })
    name: string;

    @Column()
    age: number;
}
~~~

さらにAppModuleに先ほど書いたDBの接続情報にこのEntityを使うことを教えてあげます．

~~~typescript:app.module.ts
TypeOrmModule.forRoot({
			// 一部省略
      database: 'testdb',
      entities: [User], // <-追加
})
~~~

UserModuleには以下を追加します．

~~~typescript:user.module.ts
imports: [TypeOrmModule.forFeature([User])],
~~~

そして，Repositoryの準備です．UserServiceを以下のように書き換えます．コピペでOKです．

~~~typescript:user.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "./user.entity";
import { Repository } from "typeorm";
@Injectable()
export class UserService {
    constructor(@InjectRepository(User) private readonly repository: Repository<User>) {}
    sample(): Promise<Array<User>> {
        return this.repository.find();
    }
}
~~~

さて，ここで`npm run start`して`localhost:3000/user`にアクセスするとエラーが出ます．

エラー内容は`ER_NO_SUCH_TABLE: Table 'testdb.user' doesn't exist `です．そりゃあそうですね，テーブルまだ作って無いですから．

## マイグレーション機能でテーブルを作る

EntityなどからDDLを生成することができます．

そのために一旦先ほども書いたDBの接続情報を以下のように./ormconfig.tsにも書きます．

~~~typescript:ormconfig.ts
module.exports = {
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'testuser',
    password: 'password',
    database: 'testdb',
    entities: ['src/**/*.entity.ts'],
    migrations: ['db/migrations/*.ts'],
    synchronize: false,
    cli: {
        migrationsDir: 'db/migrations'
    }
}
~~~

そしてpackage.jsonのscriptsに以下を追加します

~~~json
"typeorm": "ts-node ./node_modules/.bin/typeorm --config ormconfig.ts"
~~~

~~~bash
npm run typeorm -- migration:generate -n Initialize
~~~

すると./db/migrationsに'数列-Initialize.ts'というファイルが生成され，中にはDDLが書かれていることがわかります．

そして，以下のコマンドを実行するとテーブルが作成されます

~~~bash
npm run typeorm migration:run
~~~

~~~mysql
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| migrations       |
| user             |
+------------------+
~~~

そして再度`npm run start`すると起動できます

（もしかするとエラーが出るかもしれませんが，AppModuleのprovidersにUserServiceが指定されていたら消してください．自分の環境ではそれで治りました）

これで接続完了です．テーブルに適当にinsertしてから`localhost:3000/user`にアクセスするとinsertした内容が出力されるはずです．