--
--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Tag:

01
2018

TypeScript用Webフレームワーク「Nest」を使ってみた

CATEGORYJavaScript
TypeScript用のWebフレームワーク、いいのが見つからずにずっと昔からのexpressでやってたのだけれど、暫く前にTwitterで「Nest」なるフレームワークを知り、評判も良さそうで実績もあるようなのでちょっと試してみることにしてみた。以下、半月ほど使った感想とかそういう話を書いてみる。

expressの問題点

まず現行のexpressの問題点(不満点)はこんな感じ。
  1. 基本設計が古い。Promise登場前なのでcallbackばかり。
  2. JavaScriptのフレームワークである。TypeScriptの型をあまり活かせない。
  3. 低レイヤーの機能しか提供しないので、その上のレイヤーがプロジェクトごとにオレオレになりがち。
expressは良くも悪くも超軽量フレームワーク…というか、JavaでいうServletのような基本機能しか提供しない存在なので、自由度は高いもののちょっと心許ない。

Nestのメリット

で、それを踏まえてNestを試してみて感じたメリットを。
  1. 今風。asyncやデコレーターといった新しい機能を活用して作られている。
  2. WebSocketやその他マイクロサービスの仕組みも提供している。
  3. DIベースなので、ユニットテストが書きやすい。
  4. 裏側はexpressなので、expressのミドルウェアを使ったり、いざとなれば直接expressを叩いたりもできる。
今やってるプロジェクトはREST APIとWebSocket APIで二種類のオレオレが同居してる感じだったので、これが統合できたのが個人的には一番嬉しい(ちょっと手間はかかったけど)。

Nestのデメリット

逆にここは微妙かも…と思う点も何点かあったのでその辺り。
  1. 今後普及するかはまだまだ謎。expressはデファクトスタンダードだから安牌だけど、Nestがそれなりの地位を獲得するかはまだまだ不明。
  2. バリデーションとSwaggerを使うとデコレーター地獄になる。Javaのアノテーション地獄再び。
  3. バリデーションとSwaggerを使うと型がガチガチになる。JavaScript+αの感覚だと戸惑う。
  4. アプリ構造が、Angular的なコンポーネントベースなので、サーバーサイドとしては一般的ではない?
まあNestの普及具合が…といっても、expressの上にオレオレフレームワーク乗せるぐらいならそれよりは間違いなく上なので、オレオレになりそうだなと思ったら、素直に乗っかるのがいいと思う。
デコレーター地獄についても、結局はどこか他のところに書くわけだし…。


という事で、デメリットと感じる部分はいくつかあるものの、利便性から自分はNestに乗り換えてみることにしてみた( ̄ー ̄)b

Nestを使う上での考察

次いで、ここまで使ってみた中で、ここはこういう風に使った方がいいかも?みたいな点がいくつかあったので、それも書いてみる。今後より使い込んだら変わってくるかもしれないけどご了承ください。

型 + class-validator + swaggerで使う

Nestを使う際は、型をガン無視してanyとかにしてしまえば、ただのベターexpressとして使う事も出来そう。デコレーター地獄とかも無くなるし毎回型定義する必要もなくなる…けど、それだといろんな機能が使えないし、TypeScriptの良さも活かせないので、やっぱり型は書くべきだと思った。

で、一度型を書くと決めると芋づる式に、型にデコレーターでバリデーションを定義するclass-validatorとか、型にデコレーターでSwagger定義を付ける@nestjs/swaggerといった、型ベースの機能が活きるようになる。というか、こいつらを使うためには型をきちんと定義せざるを得ない。

バリデーションの説明には他にもJSONスキーマで行う方法が提示されてるけど、そっちを使う場合、型も厳密にすると二重定義になる感じで微妙だと思った。
下手に混在させず、型に任せるなら全部型ベースで、と割り切った方がよさげっぽい。

…ただし、全部真面目に付けると、単純なAPIでもこれぐらいデコレーターまみれになる。好き嫌いは分かれそう。
class CreateAdministratorBody {
@MinLength(1)
@ApiModelProperty({ description: '管理者名' })
name: string
@IsIn(Administrator.ROLES)
@ApiModelProperty({ description: 'ロール', enum: Administrator.ROLES })
role: string;
@IsOptional()
@ApiModelPropertyOptional({ description: '注釈' })
note?: string;
}

@ApiUseTags('admin/administrators')
@Controller('api/admin/administrators')
export class AdministratorsController {
@ApiOperation({ title: '管理者登録', description: '管理者を新規登録する。' })
@ApiCreatedResponse({ description: '登録成功', type: Administrator })
@ApiBadRequestResponse({ description: 'パラメータ不正', type: ErrorResult })
@ApiForbiddenResponse({ description: '権限無し', type: ErrorResult })
@ApiConflictResponse({ description: 'name重複', type: ErrorResult })
@UseGuards(AuthGuard)
@Post()
async create(@Body() body: CreateAdministratorBody): Promise<Administrator> {
...
}
}
※ ↑はSwaggerのデコレーター多し。バリデーションだけならもっとシンプルになります。

DIは無理に使わなくてもいいかも

今回はORMに使い慣れたsequelize-typescriptを使用している。
で、SequelizeだとModelはNestが想定するリポジトリを使う形じゃなくて、ActiveRecordでよくある単にModelクラスのstaticメソッドを使う形になる。
一応公式ドキュメントではこのstaticメソッドをリポジトリに読み替える方法が提供されている…のだけれど、ぶっちゃけいまいち利点を感じられなかった。
いや、リポジトリだとDI出来るから、ユニットテストでモックを使えるようになるよ、というのはメリットなのだけど、Sequelizeの場合DBをsqliteとかにしてしまえば普通にDBを使ったユニットテストが書けるので、あまり必要性を感じない。
(JSの場合やろうと思えばstaticメソッドでも Model.findAll = () => {...} みたいにモック使えるし。)

かつ、コントローラ以外の共通処理やバッチとか、DBを参照したいけどいちいちDIコンテナ経由で作るのは面倒な場合もある。
(特に、今回は完全新規ではなく、作りかけのexpressアプリを途中で移行したので。)

と考えると、別に無理にDI使うようにする必要なくね?という結論に至り、モデル層から先は素のsequelizeで行く方針となった。
Nestは自身でサービス層/モデル層を提供しないので、この辺は柔軟に運用していいと思う。

その他、configとかloggerとかも、無理にサービスにしてDIしなくてもいいかも。
(自分はnode-configとかlog4js-nodeとかを使用。)

CLIはあんまり使えない?

Nestでも、人気のangular-cliのごとく@nestjs/cliってのが提供されてて、これで空のプロジェクトを作ってそこにアプリを乗せていったのだけれど、この雛形はなんかあまりイケてない気がする…?
angular-cliは、フロント側はユニットテストもビルドも大変でそれを自動化してくれてメリットを非常に感じたのだけれど、サーバー側は代替手段もたくさんあるので、こちらも無理に使わなくてもいいかも。
最初だけはお世話になったけど、結局ほとんど差し替えてしまった。
(お気に入りのpower-assertとかpm2とか使いたいし。)

MiddlewareよりInterceptorを使おう

NestにはAPI呼び出しに割り込む処理として、MiddlewareInterceptorという異なる仕組みが存在する模様。
っていうか、パラメータを見るとお察しの通り、Middlewareというのは実体はexpressのMiddlewareのようだ。

なので、MiddlewareはHTTPのAPIに対してしか使用できない。NestはマイクロサービスのAPIにも対応しているけど、そちらでは使えない。一方、Interceptorはどちらでも使える。なので、新規に作る場合はInterceptorを作りましょう。

ただし一点だけ注意。MiddlewareはGuardより前に動くけど、InterceptorはGuardより後で動く。なので、認証前に必要な処理などはInterceptorでは実装できないはず。この場合はMiddlewareを使いましょう。


以上、いろいろと思いつくところを書いてみた。いまだデファクトの無いサーバーサイドTypeScript界隈、Nestが今後躍進していくのか注目したい。
スポンサーサイト

Tag: TypeScript Nest

0 Comments

Leave a comment

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。