Table of Contents
環境
- Node.js: 12.6.0
- @nestjs/cli: 6.5.0
パッケージのインストール
webpackなどHMRを有効にするためのパッケージをインストールします。
ドキュメントでインストールしているパッケージに加え、webpackの型定義ファイル(@types/webpack-env
)もインストールしています。
$ npm install --only=dev webpack webpack-cli webpack-node-externals ts-loader @types/webpack-env
webpack設定
// webpack.config.js
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: ['webpack/hot/poll?100', './src/main.ts'],
watch: true,
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?100'],
}),
],
module: {
rules: [
{
test: /.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
mode: 'development',
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.WatchIgnorePlugin([/\.js$/, /\.d\.ts$/]),
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'server.js',
},
};
ドキュメントとの設定に new webpack.WatchIgnorePlugin([/\.js$/, /\.d\.ts$/])
を追加しました。
.js
と型定義ファイルの.d.ts
は変更を検知する必要がないので外すようにしました。
変更検知の間隔を遅くしたい際は、webpack/hot/poll?100
を100から300に変えるといいと思います。
HMRの有効化
NestJSの開発サーバー側でHMRを有効化する必要があります。
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
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();
ドキュメントではwebpackの module
変数の型エラーを回避するため、
declare const module: any;
の記述をしていましたが、 @types/webpack-env
をインストールしているためここでは必要ありません。
webpackサーバー起動設定
最後にファイルの変更検知をし、HMRを実行するためのwebpackサーバーを起動をnpmコマンドで実行するための設定を行います。
"scripts"
オプションにある"start"
を更新、 "webpack"
コマンドを追加します。
// package.json
{
"scripts": {
...
"start": "node dist/server",
"webpack": "webpack --config webpack.config.js",
...
}
これでHMR環境が整いました。
webpackサーバー起動
webpackサーバーの起動コマンドを実行して、ファイルの変更検知 + HMRを行います。
$ npm run webpack
その後、ターミナルを別タブなどで開いて、NestJSの開発サーバーを起動します。
$ npm run start
この状態でどれかファイルを変更すると、コンパイルが走りアプリケーションが挙動も変更されていると思います。
npm run start:dev
で開発する時よりも明らかにコンパイルが速いはずです。
TypeORMでデータベースに接続できない問題
TypeORMを導入しているプロジェクトでHMR環境で開発している場合、以下のようなデータベースに接続できないエラーが発生します。
EPERM: operation not permitted, scandir ...
これは以下のように、データベースの設定を書いていた場合 __dirname
がHMRの時にうまく機能していないのが原因です。
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { join } from 'path';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'MY_HOST',
port: 3306,
username: 'MY_USERNMAE',
password: 'MY_PASSWORD',
database: 'MY_DATABASE',
entities: [join(__dirname, '**/**.entity{.ts,.js}')],
synchronize: true,
})
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
この解決方法として以下のようなワークアラウンドができます。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { getMetadataArgsStorage } from 'typeorm'; // <-変更
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'MY_HOST',
port: 3306,
username: 'MY_USERNAME',
password: 'MY_PASSWORD',
database: 'MY_DATABASE',
entities: getMetadataArgsStorage().tables.map(tbl => tbl.target), // <- 変更
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ファイルからではなくTypeORMの getMetadataArgsStorage()
メソッドからテーブルを取得、そこから紐づいているクラスを呼び出して解決しています。
参考: https://github.com/nestjs/nest/issues/755#issuecomment-496793495
最後に
フロントエンドをTypeScriptでの開発を行っていくことで、より堅牢でリッチなアプリケーションを作ることができます。
同様に、バックエンド側も合わせてTypeScriptにすることは処理やデータ構造の共通化をするという意味でもかなり魅力的なことである思っています。
その中でもNestJSはAngularに影響を受けているためか、きっちりした設計でバックエンドアプリの開発を行えるので注目してるのでこれからもNestJSについては書いていこうと思います。
次回は書くと言って書いていなかったTypeORMについての記事を書きたいと思います。
Related Posts
Daiki Urata
2023/05/22
Daiki Urata
2023/01/05
Daiki Urata
2022/08/29