36
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-03-29

はじめに

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に以下設定を追加。

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

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

こんな感じの階層を想定しています
tsconfig.json
./src
  + app.ts
  + controllers/UserController.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が好きです。

いろいろ補足

コントローラーのタイプ

@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も(英語ですが)充実しているので、
導入してみてしっくり来るようならいろいろ試してみて下さい。

36
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?