【NestJSアドベントカレンダー】NestJS & TyoeORMをWebackでつかう!!!【21日目】
NestJS & TypeORM & Webpackで優勝したい!
NestJsはmoduleが膨れ上がっていくと, ts-nodeだと起動がめちゃくちゃ遅くなって開発時にすごいストレスになります。それを解決できるのがHMRで、webpackです
はじめに結論
entities: getMetadataArgsStorage().tables.map(tbl => tbl.target)
をTypeOrmModule.forRootのコンフィグに設定すれば行けます
今回のリポジトリです。
NestJS & TypeORMの環境を作る
プロジェクトを作って, 必要なものをいれる
nest new sample cd sample yarn add @nestjs/typeorm typeorm mysql class-transformer class-validator
ormconfig.jsonをつくる
ormconfig.jsonにしたがってmigrationファイルとかentiiyファイルをtypeorm cliが動きます。 typeorm cliがあるとmigrationファイルを生成したり、entityファイルを生成できるので設定しておきましょう。
{ "type": "mysql", "host": "localhost", "port": 3307, "username": <user_name>", "password": <mysql_password>, "database": <database_name>, "entities": ["src/entities/*.ts"], "migrations": ["src/migrations/*.ts"], "synchronize": false, "cli": { "migrationsDir": "src/migrations", "entitiesDir": "src/entities" } }
cliについてはこちらでより詳細に見れるかと思います。
app.module.tsにtypeormをimportする
先ほど作ったjsonを元にTypeOrmModuleがデータベースと接続を試みます。
import { Module } from '@nestjs/common'; import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import ormConfig from '../ormconfig.json'; @Module({ imports: [TypeOrmModule.forRoot(ormConfig as TypeOrmModuleOptions)], controllers: [AppController], providers: [AppService], }) export class AppModule {}
以下のようなエラーが出るときはdatabase周りの設定が間違っているので見直してみましょう
[TypeOrmModule] Unable to connect to the database. Retrying
一応これでTypeORMが使えるようになってWebpackでビルドの方に行ってもいいのですが、どうせならentityとマイグレーションとCR(UD)を作って、Webpackでビルドしても動くことを確かめましょう。
User Entityを作る
package.jsonのscriptsに
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js"
を追加しておきましょう。yarn typeorm ...
でコマンドを使えるようになって便利です。
Userエンティティを作っていきます。
yarn typeorm entity:create -n User
Userには名前を保存するようにしましょう
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id!: number; @Column('varchar') name: string; }
エンティティ作成おわり
Migrationを作る
yarn migration:generate -n CreateUserTable
これだけでentities配下にあるファイルとデータベースの差分を計差してtimestamp-CreateUserTable
という名前でファイルが作られます。
これをデータベースに反映させたい時は以下のコマンドでできます
yarn migration:run
データベースにはmigrationsテーブルが勝手に作られそこでどこまでマイグレーションしたかを保存しているみたいです。
UserCR(UD)をつくる
nest g mo user nest g s user nest g co user
- user.module.ts
import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { UserService } from './user.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from '../entities/User'; @Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UserController], providers: [UserService], }) export class UserModule {}
- user.service.ts
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { User } from '../entities/User'; import { Repository } from 'typeorm'; import { CreateUserDto } from './dot/createUser.dto'; @Injectable() export class UserService { constructor(@InjectRepository(User) private readonly userRepository: Repository<User>) {} async findById(id: number) { return await this.userRepository.findOne(id); } async create(createUserDto: CreateUserDto) { return this.userRepository.save(createUserDto); } }
- user.controller.ts
import { Controller, Get, Param, Post, UsePipes, ValidationPipe, Body } from '@nestjs/common'; import { UserService } from './user.service'; import { CreateUserDto } from './dot/createUser.dto'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Get(':id') async findUserById(@Param('id') id: string) { return this.userService.findById(parseInt(id, 10)); } @Post() @UsePipes(new ValidationPipe()) async create(@Body() createUserDto: CreateUserDto) { return this.userService.create(createUserDto); } }
- src/user/createUserDto.ts
import { IsString, IsNotEmpty } from 'class-validator'; export abstract class CreateUserDto { @IsString() @IsNotEmpty() name: string; }
実行する
これで動けばNestJSとTypeORMの設定は終わりです。
yarn ts-node src/main.ts
WebpackでNestJSをビルドする
without cliをやっていきます docs.nestjs.com
必要なライブラリをインストールする
yarn add -D webpack-node-externals webpack webpack-cli webpack-node-externals ts-loader
webpackでHMRをやる
const webpack = require("webpack"); const path = require("path"); const nodeExternals = require("webpack-node-externals"); module.exports = { entry: ["webpack/hot/poll?100", "./src/main.ts"], target: "node", externals: [ nodeExternals({ whitelist: ["webpack/hot/poll?100"], }), ], module: { rules: [ { test: /.tsx?$/, use: "ts-loader", exclude: /node_modules/, }, ], }, resolve: { extensions: [".tsx", ".ts", ".js"], }, plugins: [new webpack.HotModuleReplacementPlugin()], output: { path: path.join(__dirname, "dist"), filename: "server.js", }, };
- main.ts
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; declare const module: any; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); if (module.hot) { module.hot.accept(); module.hot.dispose(() => app.close()); } } bootstrap();
これをしてyarn webpack --config webpack.cofig.js
してビルドできれば成功なのですが、できません。
こうなると思います。これはormconfig.jsonのentitiesに書いてあるファイルをTypeORMModuleが読みに行こうとするが、commonjsでないため起動できないという感じになります。
これをどう解決するかが、本題なのですが長くなりました。すみません。
答えはこちらです。
app.modules.tsのTypeOrmModuleでconfigを読み込むところを以下のように変更してください。
import { Module } from '@nestjs/common'; import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { UserModule } from './user/user.module'; import { getMetadataArgsStorage } from 'typeorm'; import ormConfig from '../ormconfig.json'; const { cli, migrations, ...typeOrmConfig } = { ...ormConfig, entities: getMetadataArgsStorage().tables.map(tbl => tbl.target), // < --これを設定することで }; @Module({ imports: [TypeOrmModule.forRoot(typeOrmConfig as TypeOrmModuleOptions), UserModule], controllers: [AppController], providers: [AppService], }) export class AppModule {}
できた
おわりに
自分の使っているプロジェクトでやってみたんですけど、起動時間が大幅に短縮されています。
明日は @potato4d さんのExpress + TypeORM NestJS 移行です