ここ数年まともにサーバーサイドをやっていないなぁとかおもいつつなんやかんやあってNestJSを数日ほど触ったりドキュメントを読んだりしてたので初心を忘れないようにメモ。
いちおうTypeORMだけは出始めにちょっと使ってた。
理解度が高まったら気分が変わるかもしれないです。そしたらもっかい日記書きます。
てか全体的に難易度高くないこのフレームワーク?求められるリテラシー高くない?
お気持ち表明
前提なんですが、僕はこのフレームワークのことをまだあまり好きになれていません。オブラートを外してのべると、すでに半分くらい嫌いです。
良いとおもったところ
色々なライブラリをつけ外しして使える
つまりNestJS自体が提供している機能はそれほど多くないと言う意味ですね。
そこそこ柔軟に書ける
↑で述べているように、プラガブルなんで例えばORMだってTypeORM使ってもいいしPrisma使ってもいい。認証とかも選べるし、意味不明なgemを入れてようわからん設定をいじるとプロジェクト全体に副作用が入ってなんかいい感じになるよ~みたいなことも基本的には……まぁ少ないかな……。少なくともprototype汚染みたいなことはしない。
プロジェクトの構成もDjango風とRails風(であってるかな?)みたいな感じのオプションがあるし、ビルドのやり方もtscとwebpackで選べる。
あれ?サーバーサイドのコード書いてたはずなのにwebpack.config.jsの編集……?まさかスタンド攻撃は"スデに"はじまっていた?一応TypeScript
「一応」だなんて歯に物の挟まった書き方がなんとなく伏線を感じさせますが、当然plainなJavaScriptでコードを書かされるよりかはマシです
公式ドキュメントが結構充実してる
大抵のことはちゃんと解説してくれているので、疑問に思ったことはおおむね公式で完結できる。
これはとても重要。
チュートリアルをやっている最初の数時間とかはかなり手触りの良いフレームワークで、「なんでこんな完成度高いのに流行らないんだ!!!!」とおもったり、そんな時代がオレにもありました。
良くないとおもった、嫌いなところ
デコレーター使いすぎ
あんま同意してくれる人がいなくて悲しいんですが、いまECMAScriptにあがっているデコレーターはあまり出来が良くない*1です。
まず仕様が複雑過ぎます。Pythonと同じ、ただの高階関数に毛が生えた程度のもので満足しておけばいい*2のに、ディスクリプタの操作だのプロパティへの付与だのと夢を詰め込みすぎて、しかも何故か肝心の関数には付与できないという。デコレーターについて語っているとそれだけで文字数制限超えそうなんではしょりますが、クラス指向というものが正直時代遅れっぽい雰囲気になっている世の中で、さらに歴史的経緯から抽象度や、TypeScriptの型検査との親和性の低さなどについて問題を抱えているクラスという微妙な機能ーーにしか適用できない実験的機能。それがデコレーターです。
しかもNestJSはそのデコレーターにフルに依存しているだけでなく、デコレーターの中でもTypeScriptが先走って実装してるParametersDecoratorとかもフルに悪用してごちゃごちゃやってます。なのでbabelではコンパイルできませんし、今後仕様の方向転換があったり、TypeScriptチームが先走り実装を反省してexperimentalDecorators
のフラグを落としたりしたら……どーなっちゃうの?!(いやどうすんのマジで)デコレーターのせいで型安全性が損なわれている
半分くらいTypeORMとかの問題になるんですが、フレームワークとかが利用するために付与するメタデータ部分はほぼ全て型安全ではありません。
どういうことかというと、例えばcreatedAtとかの定義をまとめたTimestamp
というembedded entityを作ったとしてtypescript @Column((_type) => Timestamp) timestamp!: string;
というようなアホなタイポをしてもなんのコンパイルエラーもでません。僕がアホなのでこういうことをしょっちゅうやらかすのでこれは困ります。
最初はTypeORMの型定義がアホなのかとおもったのですが、どうもデコレーターの機能不足が原因みたいです。
デコレーターに依存している部分は一事が万事この調子なので、いまのうちからリファクタリングするときのことを考えると頭が痛いです。
正直、typescript "field": "timestamp", "type": Timestamp,
みたいな、Swaggerを彷彿とさせる素朴なObjectを定義していったほうがいいんじゃないか…そんな気すらしていますというかたぶん
@Entity
とか@ObjectType
では間違いなく内部で同じような処理をしているので、そのへんのAPIを呼び出してMappedTypeでentityの型だけを取り出して使おうと計画しています。全体的にあんまり型安全性に興味なさそう
サンプルコードとかIssueでやりとりされているコードとか、コミュニティで飛び交っている雰囲気をみるとみんなすげえカジュアルにany使うやん?ジェネリクス使わん?というかそれstrict mode切ってない?みたいな感じで、Reactのフロントエンドからやってきた人間には結構厳しい。
DIまわりのデコレーターが便利じゃない
typescript @Module({ imports: [DatabaseModule.forRoot([User])], exports: [DatabaseModule], }) export class AppModule {}
みたいなのやたら書かされるけど、フレームワーク内部のDIのためだけにimportとかexportの仕組みがあるのって明らかにやりすぎだし、別にデコレーター使わなくても普通に同じことできそうだし、むしろデコレーターのせいで型の情報が消え去るし……
どのへんにメリットがあるのでしょうか。教えて偉い人。
まとめ
だいたいデコレーターのせい。
たしかAngularJSにインスパイアされて設計したとか初期の方に宣伝してたから、AngularJSに詳しい人ならこの辺のデメリットとうまく付き合う方法を知ってるのかもしれない。
型安全性に漏れのあるTypeScriptプロジェクトは大いに生産性やモチベーションを下げるのですが、いまのところこれに変わるようないい感じのフルスタックフレームワークもなさそうですし、NestJSが提供してる豊富なドキュメントや拡張例は本当に貴重なんですよね。
もうちょい掘って、せめてもうちょい保守性が高い書き方とかを確立できたらいいな~~。
そして、そういう自己流みたいなカスタムした書き方を許してくれそうなあたりがNestJSの良いところであり敷居を上げているところなのかなーとか。そんなことをおもいました。
という日記でした。