typeORM + Express で NodeJS バックエンドするメモ

`Express` + `TypeScript` + `MySQL` +  `typeORM` + `routing-controllers` でのサーバーサイド開発の雰囲気を掴むために知識を埋めていく過程のログです。

```
node: 12.2.0
typeorm: 0.2.17
typescript: 3.4.5
```


## インストールと Typescript の設定

必要なパッケージのインストール。

```shell
yarn add mysql typeorm reflect-metadata
yarn add --dev @types/node nodemon ts-node typescript
```


`tsconfig` はゆるく作ります。

```tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["es2019"],
    "sourceMap": true,
    "outDir": "./build",
    "incremental": true,
    "removeComments": false,
    "downlevelIteration": true,
    "strict": true,
    "noImplicitAny": false,
    "strictNullChecks": true,
    "strictFunctionTypes": false,
    "strictBindCallApply": false,
    "strictPropertyInitialization": false,
    "noImplicitThis": false,
    "moduleResolution": "node",
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true
  }
}
```

`package.json`に run & watch を追加します。

```package.json
...
"scripts": {
  "start": "nodemon --exec ts-node -- ./src/index.ts"
},
...
```


## typeORMのQuick Start
ここから[typeORMのQuick Start](https://typeorm.io/#/) を進めます。`typeorm`のコマンドを使ってプロジェクトを作るように指示されます。すでにプロジェクトを作っていたので `typeorm init --database mysql` しました。

> You can also run `typeorm init` on an existing node project

`typeorm init`すると`tsconfig.json`と`package.json`が上書きされ、`typescript`関連モジュールのバージョンがダウングレードされてしまいます。これは嬉しくないので`package.json`と`tsconfig.json`の変更を捨てて手動で変更します。

変更を捨てて`tsconfig.json`に以下を追加する必要があるようです。

```tsconfig.json
...
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
...
```

### 動作確認
Quick Start に戻って指示通り`ormconfig.json`にデータベースの情報を入れるとコードを動かせます。

`npm run start`して起動確認、データベースに正常に書き込まれているかどうかも `mySQL Workbench`などで確認します。
指定したデータベースに`User` テーブルが追加されユーザーが追加されていれば正解です。


### @Enityでテーブルを定義する
@Enityでテーブルを定義できます。`column`の型は自動で入りますが、指定することもできます。参考: [Column types](https://typeorm.io/#/entities/column-types)

```entity/Photo.ts
@Entity()
export class Photo {
    @PrimaryColumn()
    id: number;

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

    @Column("text")
    description: string;

    @Column("double")
    views: number;

    @Column()
    isPublished: boolean;
}
```

ここで気になったのが`entity`を変更するとデータベースのテーブルも変更してしまう点です。レコードがあろうと`entity`を変更して実行するたびにデータベースのテーブルが変更されます。これは怖い。

この動作は`ormconfig.json`の`synchronize` を`false`にすることで解決することができました。

## TypeORM Migrations
上記のとおり`synchronize: false` にするとEntityのコード変更でデータベースが変更されることはなくなります。
変わりに「`Entity`を作る → `migration`ファイルの生成 → `migration`ファイルの実行」という流れにします。

### migration ファイルを作る
テーブルの追加や変更は`typeorm migration`を使ってすることになります。データベース上のテーブルとEntityの差分から`migration`ファイルを作ってくれます。

```shell
typeorm migration:generate -n changeEntity
```

ところで`SyntaxError: Unexpected token {`というimportできないエラーがでてしまいました。ググったところts-nodeで解決するようなので `./node_modules/.bin/ts-node ./node_modules/.bin/typeorm migration:generate -n changeEntity` します。

### migration ファイルを実行する

generateしたmigrationファイルを実行します。

```
typeorm migration:run // したい
./node_modules/.bin/ts-node ./node_modules/.bin/typeorm migration:run
```

`./node_modules/.bin/ts-node ./node_modules/.bin/typeorm`はめんどうなので他の解決作を探したいところですが。時間がもったいないので、そのまま `npm scripts` に入れておきます。

参考: [Migrations - typeORM](https://typeorm.io/#/migrations)


## typeORM + routing-controllers

最後に`routing-controllers`を試します。`typeorm-routing-controllers-extensions`があるのでこれを導入していきます。 [https://github.com/typeorm/typeorm-routing-controllers-extensions](https://github.com/typeorm/typeorm-routing-controllers-extensions) のReadmeを参考に進めました。

必要なパッケージなどをインストールします。

```shell

yarn add express typeorm-routing-controllers-extensions routing-controllers
yarn add --dev @types/express
```

`typeORM`の Quick Start で作ったデータベースとデータを使って`routing-controllers`を試していきます。

ここで`src` 以下のファイル構成はおさらいしておきます。

```
src/
　├ entity/
　│ └ User.ts
　├ migration/ ...
　├ index.ts             ← 変更します。
　└ controller/          ← 新規ディレクトリ。
    └ UserController.ts  ← 新規ファイル。
```

`UserController`を作って`index.ts`から読み込みます。

```controller/UserController.ts
import { Get, JsonController } from 'routing-controllers';
import { User } from '../entity/User';
import { EntityFromParam } from 'typeorm-routing-controllers-extensions';

@JsonController()
export class UserController {
  @Get('/users/:id')
  get(@EntityFromParam('id') user: User) {
    return user;
  }
}
```


```index.ts
import 'reflect-metadata';
import { createConnection } from 'typeorm';
import { createExpressServer } from 'routing-controllers';
import { UserController } from './controller/UserController';

createConnection()
  .then(async connection => {
    createExpressServer({
      controllers: [UserController]
    }).listen(3000);
    console.log('Server is up and running on port 3000.');
  })
  .catch(error => console.log('Error: ', error));
```

`npm run start` してブラウザから`http://localhost:3000/users/1`すると確認することができます。

その他 [GitHub - typestack/routing-controllers: Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage in Express / Koa using TypeScript and Routing Controllers Framework.](https://github.com/typestack/routing-controllers) に詳しく書いてあります。

---
後半が雑になってしまいましたが。これくらいからドキュメントを読んでいけばサクサクすすめられそうです。`typeORM` + `routing-controllers` はシンプルで気にいったので、しばらく使ってみようと思います。


## 参考

* [TypeORM](https://typeorm.io/#/)
* [Quick Start - typeORM](https://typeorm.io/#/)
* [Column types - typeORM](https://typeorm.io/#/entities/column-types)
* [Migrations - typeORM](https://typeorm.io/#/migrations)
* [typestack/routing-controllers - github](https://github.com/typestack/routing-controllers) 
* [typeorm/typeorm-routing-controllers-extensions - github](https://github.com/typeorm/typeorm-routing-controllers-extensions)