GAミント至上主義

Web Monomaniacal Developer.

NestJSの型定義をNuxt3で使えるようにエクスポートする

フロント、サーバーともにTypeScriptのWEBアプリケーションにおいて、 NestJS側のEntityやDTO、GraphQLの型をNuxt3側で使えると非常に便利だったのでメモ。

システム全体像にとしてはこの記事参照 uyamazak.hatenablog.com

いろいろ前提

レポジトリはNestJS、Nuxt3は同一で下記のようなディレクトリとします

.
├── types # 共有する型定義置き場
├── front # Nuxt3のディレクトリ
└── server # NestJSのディレクトリ

Nuxt3のバージョンは

    "nuxt": "3.0.0-rc.8",

NestJSのバージョンは ここらへん

    "@nestjs/apollo": "^10.0.16",
    "@nestjs/common": "8.0.9",
    "@nestjs/config": "2.1.0",
    "@nestjs/core": "8.4.7",
    "@nestjs/graphql": "10.0.16",
    "@nestjs/platform-express": "8.4.7",
    "@nestjs/typeorm": "^8.1.4",

Entity、DTOなど型定義の共有

編集時などよくEntityそのままの値を返すことがあり、Nuxt側のfetchやuseAsyncDataなどに指定できると便利です。

POST、PUTなどのリクエスト時はDTOの型が使えると補完や型チェックができて非常に便利です。

そもそもDTOはなんぞやって場合は下記参照。 NestJS の DTO と Validation の基本 - 型定義とデコレータで安全にデータを受け付ける - Qiita https://docs.nestjs.com/openapi/types-and-parameters#types-and-parameters

出力専用のtsconfingを用意します

server/tsconfig.shared.json

{
  "extends": "./tsconfig.json",
  "include": [
    "./src/dto",
    "./src/entity",
    "./src/types.ts",
    "./src/param",
    "./src/class-validator-jp.ts"
  ],
  "compilerOptions": {
    "outDir": "../types"
  }
}

メインのやつをextendsしつつ、includeで必要なところだけを指定、outDirで上記の出力先ディレクトリを指定します。

毎回手打ちは面倒なので出力コマンドを server/package.jsonに用意しておきます。

  "scripts": {
    "export:types": "rimraf ../types && tsc -P tsconfig.shared.json --emitDeclarationOnly",
  },

古いゴミが残らない用に書き出し前にrimrafを使って削除を行っています(NestJSのデフォルトprebuildで使ってたので流用) npm run のスクリプトの中でディレクトリの削除を行う (rimraf) - まくまくNode.jsノート

先程用意した出力用tsconfigを指定し、

型定義だけでいいので

--emitDeclarationOnly

を指定します。 これを指定しないと値も出力されるわけですが、それすなわち使用するfront側でもNestJSやその他サーバー側ライブラリのインストールが必要になってしまうので現実的ではありません。

yarnを使っているのでserverディレクトリで

yarn export:types

で出力されるようになります

index.tsも自動生成する

ファイル数が増えてくると、import時に全部指定するのが面倒なのでディレクトリごとにindex.tsがあると便利です。

自動生成してくれる下記ライブラリをインストールして使っています。

create-ts-index - npm search

yarn add -D create-ts-index

して package.jsonのscriptsに下記のようなコマンドを追加して使っています。 全フォルダにはいらないので、ファイル数が多い必要な場所だけ指定しています。

    "cti": "cti create src/dto src/entity -b -s=1 -i=spec.ts",

これで

import { FugaEntiry } from '~/entity/fuga-entity.entity'

のようなfromを

import { FugaEntiry } from '~/entity/'

に省略できるようになります。

Entityの型とレスポンスの型が違う場合

TypeORMでLazy Relationsを使っているとEntityのプロパティの型はそのままPromiseになってしまいますが、 レスポンスとして返す場合はJSONですのでもちろん解決した値を返すと思います。

typeorm/eager-and-lazy-relations.md at master · typeorm/typeorm · GitHub

その場合、あまりきれいではありませんが、上書きして新しい型を作って使うようにしています。 今Nuxt3側で出力してるけど、よく考えるとサーバー側で出力した方が変更しやすくていいかもしれない。

import {
  HogeEntity,
  LazyItem
} from '~/../types/entity'

export interface HogeEntityResponse extends Omit<HogeEntity, 'lazyItems'> {
  lazyItems: LazyItem[] // 元は Promise<LazyItem[]>
}

GraphQLの型を出力する

こちらを参考にCode firstとして使っています。

https://docs.nestjs.com/graphql/quick-start

GraphQLの型を書き出してくれるこいつをインストールします @graphql-codegen/cli - npm

yarn add -D @graphql-codegen/cli

設定ファイルを追加します

server/codegen.yml

schema: ./src/schema.graphql
generates:
  ../types/graphql-types.ts:
    plugins:
      - typescript

出力先 (../types/graphql-types.ts) とpluginsでtypescriptを指定するだけで使えました。

これもpackage.jsonのscriptsに追加

    "graphql-codegen": "graphql-codegen",

上記の型定義とGraphQLの型定義は同時にやってくれたほうがいいので、つなげて実行してしまっています

   "export:types": "graphql-codegen && rimraf ../@shared/@types/server && tsc -P tsconfig.shared.json --emitDeclarationOnly",