vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

TypeOrm を追加する

はじめに

今回は TypeOrm を追加して Database にアクセスできるようにしますよ。

Angular 10 + NgRx + Nest.js でプロジェクトを作る

TypeOrm を使う

Install

前回と同じくいくつかパッケージをインストールします。

Nest.js のプロジェクトに統合するためのパッケージもあるので合わせてインストールします。

npm install --save @nestjs/typeorm typeorm pg

Configuration

前回は「ormconfig.json」を使って TypeOrm の設定( Database の接続文字列など)を行っていました。

Nest.js のプロジェクトでは、「app.module.ts」の中で設定することもできます。

server/app.module.ts


import { Module } from '@nestjs/common';
import { AngularUniversalModule } from '@nestjs/ng-universal';
import { TypeOrmModule } from '@nestjs/typeorm';
import { join } from 'path';
import { Workflow } from './entities/workflow.entity';

@Module({
  imports: [
    AngularUniversalModule.forRoot({
      viewsPath: join(process.cwd(), 'dist/browser'),
      bundle: require('../server/main'),
      liveReload: false
    }),
    TypeOrmModule.forRoot({
      type: "postgres",
      host: "localhost",
      port: 5432,
      username: "test",
      password: "test",
      database: "Example",
      entities: [Workflow],
      synchronize: true,
      logging: true,
      cache: false
    })
  ],
  controllers: [],
  providers: []
})
export class ApplicationModule {}

結局 JSON ファイルを選択した

結局、前回同様「ormconfig.json」を使うことにしました。

理由は、マイグレーションファイルを使った Database 更新をしたい場合、 TypeOrm のコマンドを使うのですが、この時コマンドからは「app.module.ts」の内容が参照できません(多分)。

ということで、いずれにしても「ormconfig.json」が必要になるわけですね。

ormconfig.json


{
    "type": "postgres",
    "host": "localhost",
    "port": 5432,
    "username": "test",
    "password": "test",
    "database": "Example",
    "entities": ["dist/**/entities/*.entity{.ts,.js}"],
    "migrations": ["server/migrations/*.{.ts,.js}"],
    "synchronize": true,
    "logging": true,
    "cache": false,
    "cli": {
        "entitiesDir": "server/entities",
        "migrationsDir": "server/migrations"
    }
}

なお「ormconfig.json」を使う場合の「app.module.ts」はこんな感じです。

server/app.module.ts


~省略~
@Module({
  imports: [
    ~省略~
    TypeOrmModule.forRoot()
  ],
  controllers: [],
  providers: []
})
export class ApplicationModule {}

Entity クラスを追加する

server/entities/workflow.entity.ts


import {Entity, PrimaryGeneratedColumn, Column, OneToMany, UpdateDateColumn} from "typeorm";

@Entity("Workflow")
export class Workflow {
    @PrimaryGeneratedColumn()
    id: number = -1;
    @Column({ type: 'text', nullable: false })
    createUserMail: string = '';
    @Column({ type: 'timestamp with time zone', nullable: true })
    circulationLimit: Date|null = null;
    @Column({ type: 'timestamp with time zone', nullable: false })
    lastUpdateDate: Date = new Date();
    
    public create(createUserMail: string, circulationLimit: Date|null) {
        if (createUserMail == null ||
            createUserMail.length <= 0) {
            throw new Error("Can not set createUserMail empty");
        }
        this.createUserMail = createUserMail;
        this.update(circulationLimit);
    }
    public update(circulationLimit: Date|null) {
        this.circulationLimit = circulationLimit;
        this.lastUpdateDate = new Date();
    }
}

「nest generate」を使う

TypeOrm のクラスにアクセスするため、いくつか Nest.js のプロジェクトにクラスを追加していきます。

が、「nest generate ~」を実行すると、ファイルが「src」ディレクトリにできてしまう。。。(´・ω・`)
サーバー側のファイルを置いている「server」ディレクトリ以下にファイルを出力するにはどうすれば・・・?

結果としては、プロジェクト直下に「nest-cli.json」を置くことで解決しました。

nest-cli.json


{
    "collection": "@nestjs/schematics",
    "sourceRoot": "server"
}

「@nestjs/testing」の追加

Controller や Service などのクラスを追加したところ、エラー発生。
どうも一緒に追加されたテストクラスが必要としている、「@nestjs/testing」が足りない模様。

ということでインストールしました。

npm install --save-dev @nestjs/testing

Repository クラスから Database にアクセスする

Database にアクセスする(失敗)

まず Database にアクセスするための Controller や Service を追加します。

server/workflow/workflow.controller.ts


import { Controller, Get } from '@nestjs/common';
import { WorkflowService } from './workflow.service';
import { Workflow } from '../entities/workflow.entity';

@Controller('workflow')
export class WorkflowController {
    constructor(private readonly service: WorkflowService) {
    }
    @Get("search")
    public async getWorkflowItems(): Promise<Workflow[]> {
        return await this.service.findAll();
    }
}

server/workflow/workflow.service.ts


import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Workflow } from '../entities/workflow.entity';
import { Repository } from 'typeorm';
import { of } from 'rxjs';

@Injectable()
export class WorkflowService {
    constructor(
        @InjectRepository(Workflow)
        private workflowRepositiory: Repository<Workflow>
    ){}
    public findAll(): Promise<Workflow[]> {
        return this.workflowRepositiory.find();
    }
}

server/app.module.ts


...
import { WorkflowController } from './workflow/workflow.controller';
import { WorkflowService } from './workflow/workflow.service';
import { Workflow } from './entities/workflow.entity';

@Module({
  imports: [
...
  ],
  controllers: [WorkflowController],
  providers: [WorkflowService]
})
export class ApplicationModule {}

OK。では実行してみます。

…エラーが発生しました(´・ω・`)

...
[Nest] 17376   - 07/11/2020, 8:22:27 PM   [ExceptionHandler] Nest can't resolve dependencies of the WorkflowService (?). Please make sure that the argument WorkflowRepository at index [0] is available in the ApplicationModule context.

Potential solutions:
- If WorkflowRepository is a provider, is it part of the current ApplicationModule?
- If WorkflowRepository is exported from a separate @Module, is that module imported within ApplicationModule?
  @Module({
    imports: [ /* the Module containing WorkflowRepository */ ]
  })
 +570ms
Error: Nest can't resolve dependencies of the WorkflowService (?). Please make sure that the argument WorkflowRepository at index [0] is available in the ApplicationModule context.

Potential solutions:
- If WorkflowRepository is a provider, is it part of the current ApplicationModule?
- If WorkflowRepository is exported from a separate @Module, is that module imported within ApplicationModule?
  @Module({
    imports: [ /* the Module containing WorkflowRepository */ ]
  })

    at Injector.lookupComponentInParentModules (C:\Users\example\Documents\workspace\proofreading-workflow\node_modules\@nestjs\core\injector\injector.js:190:19)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
[nodemon] app crashed - waiting for file changes before starting...

依存を解決する

どうも Repository クラスが DI できてないようです。

結局「app.module.ts」にモジュールを追加しました。

server/app.module.ts


...
import { Workflow } from './entities/workflow.entity';

@Module({
  imports: [
...
    TypeOrmModule.forRoot(),
    TypeOrmModule.forFeature([Workflow])
  ],
  controllers: [ProofreaderController, WorkflowController],
  providers: [ProofreaderService, WorkflowService]
})
export class ApplicationModule {}

ようやく Database にアクセスできるようになりました :).

参照