はじめに
Express.jsをTypeScriptと共に使用する場合、どうせならクラスベースで
コードを書いていきたいなぁ…となるのが自然だと思います。
MVCのモデル部分をクラスベースで書いていく件については
TypeORM関連の記事を何本か書きましたが、
本記事ではMVCのCに当たる部分、コントローラー(とrouting)について扱います。
(ライブラリ選定のメモ):
コントローラーにrouting-controllers、モデルにTypeORMを突っ込んだならば、
アプリケーション規模によってはnestJSをおすすめします。
クラスベースで書くコントローラー
さて仕組みづくりです。スクラッチで自分でやっちゃっても良いのですが
世の中には同じことを考える人がたくさんいて、やろうとしていたことは全部
routing-controllers というライブラリを使うと秒で実現できてしまったため、
今回はそちらに乗っかることにします。
導入の前に、どのようなコントローラーになるかイメージできたほうが良いと思うので、
一つサンプルコードをば。例えば、usersというエンドポイントをrouting-controllersで書くと…
ドン!
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)
セットアップ
必要ライブラリのインストール
npm install routing-controllers reflect-metadata body-parser multer --save
型情報のインストール
npm install @types/express @types/body-parser @types/multer --save
デコレーターを使用するため、tsconfig.jsonのcompilerOptionsに以下設定を追加。
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
app.ts (アプリのエントリーポイント) の作成
こんな感じの階層を想定しています
tsconfig.json
./src
+ app.ts
+ controllers/UserController.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が好きです。
いろいろ補足
コントローラーのタイプ
@JsonController('/users') // コレを使うとレスポンス形式がJSONに
@Controller('/users') // コレを使うとレスポンス形式がHTMLに
export class UserController {
ルーティング
ルーティングはコントローラーデコレーターに指定したパス+メソッドデコレーターに指定したパスとなります。
例: /users/
にルーティングする
- コントローラーデコレーターに共通ルートを書き、メソッドデコレーターにusers以下のパスを書く方式
@JsonController('/users')
export class UserController {
@Put('/:id')
put(@Param('id') id: number, @Body() user: User) {
users[id] = user
return 'ok'
}
- コントローラーデコレーターに何も書かず、メソッドデコレーターに全パスを書く方式
@JsonController()
export class UserController {
@Put('/users/:id')
put(@Param('id') id: number, @Body() user: User) {
users[id] = user
return 'ok'
}
クエリパラメーターとBodyパラメーター
- メソッド引数に
@Param
を追加するとクエリパラメーターを取得できます - 同じく
@Body
追加でBodyパラメーターを取得できます
@Put('/:id')
put(@Param('id') id: number, @Body() user: User) {
users[id] = user
return 'ok'
}
まとめ
薄くて軽量が故に自由すぎるexpress.jsにクラスとデコレーターである程度の規約をつけることで
実装上の迷いが少なくなるので、routing-controllersはとてもおすすめです。
公式のreadme.mdも(英語ですが)充実しているので、
導入してみてしっくり来るようならいろいろ試してみて下さい。