TypeScriptでExpress.jsのコントローラー部分をクラスベースで書く


# はじめに

Express.jsをTypeScriptと共に使用する場合、どうせならクラスベースで
コードを書いていきたいなぁ…となるのが自然だと思います。

MVCのモデル部分をクラスベースで書いていく件については
TypeORM関連の記事を何本か書きましたが、
本記事ではMVCのCに当たる部分、コントローラー(とrouting)について扱います。

(ライブラリ選定のメモ)：
コントローラーにrouting-controllers、モデルにTypeORMを突っ込んだならば、
アプリケーション規模によってはnestJSをおすすめします。


# クラスベースで書くコントローラー

さて仕組みづくりです。スクラッチで自分でやっちゃっても良いのですが
世の中には同じことを考える人がたくさんいて、やろうとしていたことは全部
[routing-controllers](https://github.com/typestack/routing-controllers) というライブラリを使うと**秒で**実現できてしまったため、
今回はそちらに乗っかることにします。

導入の前に、どのようなコントローラーになるかイメージできたほうが良いと思うので、
一つサンプルコードをば。例えば、usersというエンドポイントをrouting-controllersで書くと…

ドン！

```ts
import { JsonController, Param, Body, Get, Post, Put, Delete } from 'routing-controllers'

// モデルの代わりだと思って下さい。サンプル用。
type User = { name: string; age: number }
// DBの変わりだと思ってください。サンプル用。
const users = [{ name: 'hoge', age: 25 }, { name: 'fuga', age: 28 }, { name: 'piyo', age: 27 }]

@JsonController('/users')
export class UserController {
  @Get('/')
  getAll() {
    return users
  }

  @Get('/:id')
  getOne(@Param('id') id: number) {
    return users[id]
  }

  @Post('/')
  post(@Body() user: User) {
    users.push(user)
    return 'ok'
  }

  @Put('/:id')
  put(@Param('id') id: number, @Body() user: User) {
    users[id] = user
    return 'ok'
  }

  @Delete('/:id')
  remove(@Param('id') id: number) {
    users.splice(id, 1)
    return 'ok'
  }
}
```

クラスベースで構造を作り、デコレーターで振る舞いを与えています。とても直感的ですね。

 - クラスデコレーターで使用するコントローラーとルーティングパスを宣言
 - メソッドデコレーターでHTTPメソッドとエンドポイントを宣言
 - メソッド引数デコレーターでリクエストパラメーターとクエリパラメーターを取得


また、サンプルには出てきませんが、他にもデコレータを使って
リクエストヘッダ・レスポンスヘッダ・セッション・クッキーの取得、
ユーザー認証の注入、カレントユーザーの取得、expressミドルウェアとの連携、
オリジナルのインターセプターの追加などなど様々なことができるようになります。

気力があればそのへんも記事にしたいなぁ…

# routing-controllersの導入

前章をみて、試してみたくなったのではないでしょうか？

はい、それでは早速導入していきましょう。

※以下導入手順はexpress.jsとTypeScriptが導入済みのプロジェクトを想定しています。
　Dockerベースのものであれば手前味噌ですがこんな記事もあります。
　[TypeScriptでExpress.js開発するときにやることまとめ (docker/lint/format/tsのまま実行/autoreload)](https://qiita.com/yuukive/items/012bdf1b9ff3881546b3)


## セットアップ

必要ライブラリのインストール

```bash
npm install routing-controllers reflect-metadata body-parser multer --save
````

型情報のインストール

```bash
npm install @types/express @types/body-parser @types/multer --save
```


デコレーターを使用するため、tsconfig.jsonのcompilerOptionsに以下設定を追加。

```js:tsconfig.json
{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}
```

app.ts (アプリのエントリーポイント) の作成

```
こんな感じの階層を想定しています
tsconfig.json
./src
  + app.ts
  + controllers/UserController.ts
```

```ts:src/app.ts
import "reflect-metadata" // リフレクションを使用するため必須
import {createExpressServer} from "routing-controllers"
// controllersディレクトリを作成し、前章の内容を./controllers/UserController.tsに保存
import {UserController} from "./controllers/UserController"

// express appの作成。ここでオプションに登録するコントローラーを渡す
const app = createExpressServer({
   controllers: [UserController]
});

// express を port 3000 で起動
app.listen(3000);
```

## 動作確認

app.tsを実行し、`http://localhost:3000/users` にアクセスするとユーザー一覧が返ってきます。
また、Postman等を使用してPSOTやPUTやDELETEを投げればそれぞれの処理が走ります。


余談：
tsファイルの実行はトランスパイルをしてから `node ./dist/app.js` のようにするもよし、
ts-nodeをインストールして `ts-node ./src/app.ts` とするもよし。
毎回トランスパイルするの面倒なので、個人的にはts-nodeが好きです。

## いろいろ補足

### コントローラーのタイプ

```ts
@JsonController('/users') // コレを使うとレスポンス形式がJSONに
@Controller('/users') // コレを使うとレスポンス形式がHTMLに
export class UserController {
```

### ルーティング

ルーティングはコントローラーデコレーターに指定したパス+メソッドデコレーターに指定したパスとなります。


#### 例： `/users/` にルーティングする

 - コントローラーデコレーターに共通ルートを書き、メソッドデコレーターにusers以下のパスを書く方式

```ts
@JsonController('/users')
export class UserController {
  @Put('/:id')
  put(@Param('id') id: number, @Body() user: User) {
    users[id] = user
    return 'ok'
  }

```

 - コントローラーデコレーターに何も書かず、メソッドデコレーターに全パスを書く方式

```ts
@JsonController()
export class UserController {
  @Put('/users/:id')
  put(@Param('id') id: number, @Body() user: User) {
    users[id] = user
    return 'ok'
  }

```

### クエリパラメーターとBodyパラメーター

 - メソッド引数に`@Param`を追加するとクエリパラメーターを取得できます
 - 同じく`@Body`追加でBodyパラメーターを取得できます

```ts
  @Put('/:id')
  put(@Param('id') id: number, @Body() user: User) {
    users[id] = user
    return 'ok'
  }
```


## まとめ

薄くて軽量が故に自由すぎるexpress.jsにクラスとデコレーターである程度の規約をつけることで
実装上の迷いが少なくなるので、[routing-controllers](https://github.com/typestack/routing-controllers)はとてもおすすめです。



公式のreadme.mdも（英語ですが）充実しているので、
導入してみてしっくり来るようならいろいろ試してみて下さい。