Nest.jsのCRUD Generatorにカスタマイズした自前のSchematicsを追加する方法

こんにちは。最近めっちゃ寒いですね。そういえば僕は4K有機ELのテレビが欲しすぎて価格.comをみまくってます。

最近Nest.jsを趣味で使い始めてみて、CRUD Generatorがとても便利だったんですがもっと便利にしたいと思ってカスタマイズした自前のSchematicsを追加してみました。

CRUD Generatorとは?

Node.jsのサーバーサイドフレームワークであるNest.jsについているCLIの機能の1つです。

https://docs.nestjs.com/recipes/crud-generator

こんな感じでCLIでコマンドを打つだけで1リソースに対するCRUDの雛形が自動出力されます。

/img/2021-11-06.gif

実行中

/img/2021-11-06.png

出力されたファイル群

ただし、1リソースに対するCRUDといいつつもあくまで雛形なので、DBからのデータの取得・更新部分やリクエスト・レスポンスの中のプロパティなどは一切出力されません。

なにをしたか

極力CRUD generatorを使い手作業での実装を減らしたかったので、DBからのデータの取得・更新部分も出力されるようにschematicsをいじってみました。

私の環境ではDBからのデータ取得・更新部分はORMのPrisma(v3)を使っているので、それと絡めてserviceクラスを自動出力してみます。

ちなみにschematicsとは、一つの自動出力されるファイル群の単位を指してます。

どうやったか

CRUD Generatorのコマンドには--collectionというオプションがあります。そこにnode_modulesにインストール済みのパッケージを指定することで、指定のschematicsをそのパッケージ内から検索し利用します。デフォルトでは@nestjs/schematicsが指定されています。

例えばnest g resource hogeのように実行した場合は@nestjs/schematicsパッケージのsrc/lib/resource/{schematics名,この例ではresource}/files/ts/ディレクトリ内に存在する一連のファイル群が自動出力されます。このディレクトリは変更可能ですが@nestjs/schematicsの内部の仕組みを改変するのは大変なので、このパッケージの仕組みを使いまわして実現します

ちなみに、僕の知る限りで同じようにコード自動出力を行うOpenAPI Generatorだと、自前のディレクトリを用意するだけでよかったんですが、今回はnpmパッケージを用意する必要があって少し面倒でした

具体的な手順は以下の通りです。

1. @nestjs/schematicsをクローン

git clone https://github.com/nestjs/schematics.git

2. 自前のschematicsを追加

クローンしたパッケージにいくつか変更を加えます

package.json↓(後でインストールした際に@nestjs/schematicsを上書きしてしまうのは困るので)

{
-  "name": "@nestjs/schematics",
+  "name": "@diggymo/schematics",
  "version": "8.0.4",
  "description": "Nest - modern, fast, powerful node.js web framework (@schematics)",

あとは、src/libに自分でschematicsをカスタマイズしてみましょう。僕はresourceディレクトリを複製し改変しました。もし新たにschematicsを追加した場合はsrc/collection.jsonも追加で修正する必要があります

    "sub-app": {
      "factory": "./lib/sub-app/sub-app.factory#main",
      "description": "Create a Nest application (mono-repo).",
      "schema": "./lib/sub-app/schema.json"
    },
    "resource": {
      "factory": "./lib/resource/resource.factory#main",
      "description": "Create a Nest resource.",
      "schema": "./lib/resource/schema.json"
-    }
+    },
+    "resource-with-prisma": {
+      "factory": "./lib/resource-with-prisma/resource.factory#main",
+      "description": "Create a Nest resource with prisma(ORM)",
+      "schema": "./lib/resource-with-prisma/schema.json"
+    }
  }
}

3. ビルド

トランスパイルが必要なのでクローンしたディレクトリ内でビルドします

yarn
yarn build

4. node_modulesにインストール

CRUD Generatorを使いたいディレクトリに、改変した@nestjs/schematicsをインストールします

yarn add {上記で改変したパッケージのパス}

https://qiita.com/suin/items/409dc019de605830b4c2

5. CRUD Generatorを実行

-cに2の手順で設定したpackageのnameを設定してCRUD Generatorを実行します

nest g resource-with-prisma TestHogeHuga -c @diggymo/schematics

これで自前のCRUD Generatorを実行することができました🍺。serviceクラスを見ると、CRUD Generatorからカスタマイズできていることがわかります🚀

import { Injectable } from '@nestjs/common';
import { CreateTestHogeHugaDto } from './dto/create-test-hoge-huga.dto';
import { UpdateTestHogeHugaDto } from './dto/update-test-hoge-huga.dto';
+import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class TestHogeHugaService {
  constructor(private prismaService: PrismaService) {}

  create(createTestHogeHugaDto: CreateTestHogeHugaDto) {
-    return 'This action adds a new testHogeHuga';
+    return this.prismaService.testHogeHuga.create(createTestHogeHugaDto)
  }

  findAll() {
-    return `This action returns all testHogeHuga`;
+    return this.prismaService.testHogeHuga.findMany()
  }

  findOne(id: number) {
-    return `This action returns a #${id} testHogeHuga`;
+    return this.prismaService.testHogeHuga.findMany({
+      where: {
+        id
+      }
+    })
  }

  update(id: number, updateTestHogeHugaDto: UpdateTestHogeHugaDto) {
-    return `This action updates a #${id} testHogeHuga`;
+    return this.prismaService.testHogeHuga.update({
+      where: {
+        id
+      },
+      data: updateTestHogeHugaDto
+    })
  }

  remove(id: number) {
-    return `This action removes a #${id} testHogeHuga`;
+    return this.prismaService.testHogeHuga.delete({
+      where: {
+        id
+      }
+    })
  }
}

どのように記述すれば上記のように出力されるかは、こちらを確認してください。

所感

  • DBからテーブルのプロパティも取得して、リクエスト・レスポンスの中のプロパティも自動で出力までできれば理想
  • 出力されるファイルで実装されるRESTAPIは、一意なidがnumber前提だったり、リクエスト・レスポンスの型が空のクラスだったりそのままでは実用できないが、圧倒的に実装速度が変わるのではないかなと思った
  • -cオプションの存在を知った時点ですぐにできると思っていたが、調べるのにとても苦労した。正直今の手順だとOpenAPIGeneratorなどの他のコード自動出力ツールに比べて手間がかかりすぎるので改善して欲しいし、そもそもNest.jsのドキュメントに書いて欲しい…

参考リンク


See also