Closed35

NestJSの知見まとめ

ピン留めされたアイテム
uttkuttk

NestJSについての知見を得たら、随時ここに追加してまとめていく💪
ある程度まとまったら、本にする予定📚

uttkuttk

@nestjs/configについて

どんなライブラリ?

NestJSアプリで環境変数を扱えるようになります。

使い方

環境変数を使いたいModuleクラスでConfigModule.forRoot()をインポートする

hoge.mosule.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [ ConfigModule.forRoot() ], // .env ファイル をロードする
})
export class HogeModule {
  constructor( config: ConfigService ) {
    // 型を渡さないと、anyとして扱われるので注意!
    console.log( config.get<string>("HOGE_ENV") ) // "hoge"
  } 
}

Serviceクラスで使う方法

hoge.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { HogeService } from "./hoge.service";

@Module({
  imports: [ ConfigModule.forRoot() ], // .env ファイル をロードする
  provider: [ HogeService ] // HogeService でも ConfigModule を使えるようにする
})
export class HogeModule {}
hoge.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class UsersService {
  // コンストラクタにConfigServiceのインスタンスが渡される
  // 因みに引数の順番は関係ない( 理由は下の方に書いてある🏓 )
  constructor( config: ConfigService  ) {
    console.log( config.get<string>("HOGE_ENV") ); // 環境変数を読み込んで使う
  }
}

オプションについて

公式リポジトリの情報を参考にしています。

ConfigModuleのオプション一覧
import { ConfigService } from '@nestjs/config';

@Module({
  imports: [ ConfigModule.forRoot({
    // キャッシュを有効にするか。
    // envファイルの読み込みが遅い場合があるので、
    // サーバーの起動が遅いときは有効にすると良いかも
    cache?: boolean,

    // 他のクラスからのアクセスを有効にするかどうか
    // trueだと ConfigModule.forRoot() を AppModule に書くだけで他のクラスでも扱えるようになる
    // 参考 : https://docs.nestjs.com/techniques/configuration#use-module-globally
    isGlobal?: boolean,

    // envファイルから環境変数を読み込まないようにする
    // trueの場合は、envファイルの代わりにランタイム環境から取得する
    // ランタイム環境変数は、cross-envを使って設定すると便利
    ignoreEnvFile?: boolean,

    // trueの場合は、既に定義されている環境変数を検証しない
    // 「 既に定義されている環境変数 」は、ランタイム環境変数などの事
    ignoreEnvVars?: boolean,

    // envファイルへのパス
    // 絶対パスでも相対パスでも大丈夫
    // 相対パスは、実行ファイルからの位置
    envFilePath?: string | string[],

    // 環境ファイルのエンコーディング
    encoding?: string,

    // 環境変数を検証するカスタム関数
    // 環境変数を編集することも出来る( やらない方が良い )
    validate?: (config: Record<string, any>): => Record<string, any>,

    // 環境変数検証スキーマ( @hapi/joi を使える )
    // validateよりも、こっちの方が良さそう
    // 参考 : https://docs.nestjs.com/techniques/configuration#schema-validation
    validationSchema?: any,

    // 上記のスキーマに渡すオプション
    validationOptions?: Record<string, any>,

    // より複雑なファイル( ymlなど )をロードするための関数の配列
    // 参考 : https://docs.nestjs.com/techniques/configuration#custom-configuration-files
    load?: Array<ConfigFactory>,

    // trueにすると、envファイルでテンプレート文字列みたいなことが出来るようになる
    // 参考: https://docs.nestjs.com/techniques/configuration#expandable-variables
    expandVariables?: boolean,
  })]
})
export class SampleModule {}
nikaeranikaera

ConfigService について初めて知りました!👀
まだまだ勉強中の身なのですが、僕も下記で知見をまとめたので何かのご参考になれたら嬉しいなと思いました🙋‍♂️
https://zenn.dev/nikaera/books/nestjs-azure-dev

引き続きウォッチしながら NestJS で機能作成する際の参考にさせていただきたいと思います📝

uttkuttk

まだまだ勉強中の身なのですが、僕も下記で知見をまとめたので何かのご参考になれたら嬉しいなと思いました🙋‍♂️

おお!
NestJSは日本語の情報が全然無いので、とても助かります😭
後でじっくり読んで参考にさせて頂きますね。

引き続きウォッチしながら NestJS で機能作成する際の参考にさせていただきたいと思います📝

ありがとうございます!
今後も知見を得たら追加していく予定なので、是非参考にしてご自身の開発に役立てて下さい!
ある程度まとまったら記事するつもりなので、より詳細な解説はそちらの方でするかもです💁‍♂️
なので、このスクラップは気軽に見てくれると嬉しいです😆

NestJSは日本語の情報が全然無いので、
情報共有し合って、より良い開発につなげられていけば良いなと思っています💪

nikaeranikaera

こちらこそありがとうございます!☺️

NestJSは日本語の情報が全然無いので、
情報共有し合って、より良い開発につなげられていけば良いなと思っています💪

同じく NestJS の日本語情報少ないなと思っていたので @uttk さんの知見は非常にありがたいです!📝
おかげさまで知見アウトプットする自分のモチベにもなりました!💪

記事楽しみにしております!😆 (ワクワク

uttkuttk

気になったこと

気になったことを追記していく...🖊

uttkuttk

Serviceクラスのコンストラクタの引数について

事の発端

Serviceクラスのコンストラクタ引数の順番を逆にしてもちゃんと動いたので、
「 なんでだろう?🤔」ってなった。

sample.service.ts
import { Injectable } from '@nestjs/common';
import { FooService } from 'src/foo/foo.service';
import { HogeService } from 'src/hoge/hoge.service';

@Injectable()
export class SampleService {
  // コンストラクタで複数のサービスインスタンスを受け取る
  construtor(
    private hogeService: HogeService,
    private fooService: FooService
  ){}

  /*
    // 以下のように、順番を逆にしても大丈夫だった
    construtor(
      private fooService: FooService,
      private hogeService: HogeService
    ) {}
  */

  /*
    // 型が無いとコンパイルエラーになる
    construtor(
      private fooService: any, // ここでエラーが発生
      private hogeService: HogeService
     ) {}
  */

  hoge() {
    this.hogeService.hoge();
  }

  foo() {
    this.fooService.foo();
  }
}

コンストラクタの引数を順番を逆にしても大丈夫な理由

以下の文章は、公式ドキュメントより引用。

Build

nest build is a wrapper on top of the standard tsc compiler (for standard projects) or the webpack compiler (for monorepos). It does not add any other compilation features or steps except for handling tsconfig-paths out of the box. The reason it exists is that most developers, especially when starting out with Nest, do not need to adjust compiler options (e.g., tsconfig.json file) which can sometimes be tricky.

See the nest build documentation for more details.

要約すると、
nestjstscコンパイラwebpackコンパイラをラップしたコンパイラを使っている。

なので、Serviceクラスから別のServiceクラスをコンストラクタで受け取る時、型を見てコンストラクタに渡す値を判断している模様( タブン )。

そのため、引数の順番はTypeScriptで型をつけてれば関係ない。

はぇー、NestJS凄い🙄

uttkuttk

Controllerでメソッドを書くときは順番には気をつけよう!って話

結論

結論だけ知りたい人に向けて、先に結論言っておきます。

  • Controllerのメソッドの順番にはちゃんと意味があるよ
  • メソッドの順番はExpressによるものだよ
  • パラメーターを持つメソッドはなるべく下の方に記述したほうがいいよ
  • 公式ドキュメントだけじゃなくて、issuesとかも読んでおいた方がいいよ

本題

以下のようなControllerクラスを書いた。

cats.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { Cat } from './cat.entity';
import { CatsService } from './cats.service';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  // 全件取得    
  // 例: http://localhost:3000/cats/all
  @Get('all')
  async all(): Promise<Cat[]> {
    return await this.catsService.findAll();
  }

  // 指定されたIDを持つ情報だけ取得
  // 例: http://localhost:3000/cats/1
  @Get(':id')
  async findOne(@Param('id') id: Cat['id']): Promise<Cat | undefined> {
    const result = await this.catsService.findOne(id);

    return result ? result : { message: "渡されたIDを持つネコはいませんでした" };
  }

  // 最後の値だけを取得    
  // 例: http://localhost:3000/cats/last
  @Get('last')
  async getLast(): Promise<Cat | undefined> {
    const result = await this.catsService.findLast();

    return result ? result : { message: "まだ一匹も登録されていません" };
  }
}

そして、上記のコントローラーの/cats/lastに対してGETリクエストを投げた。
すると、なぜだか/cats/:idの方のメソッドが実行される。

/cats/lastを投げた時の結果
{
  "message": "渡されたIDを持つネコはいませんでした" 
}

この時、 /cats/:id の :id 部分に /cats/last の last の部分が引っかかってしまっているのだろうと予測できるが、/cats/all は普通に実行できているので、「 これはおかしい…🤔」となる。

そこで、CatsControllerの getLast() findOne() よりも先に書くように修正した。

cats.controller.ts
/* -- 省略 -- */

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  // 全件取得    
  // 例: http://localhost:3000/cats/all
  @Get('all')
  async all(): Promise<Cat[]> {
    return await this.catsService.findAll();
  }

  // 最後の値だけを取得    
  // 例: http://localhost:3000/cats/last
  @Get('last')
  async getLast(): Promise<Cat | undefined> {   // findOne()よりも上に移動した 
    const result = await this.catsService.findLast();

    return result ? result : { message: "まだ一匹も登録されていません" };
  }

  // 指定されたIDを持つ情報だけ取得
  // 例: http://localhost:3000/cats/1
  @Get(':id')
  async findOne(@Param('id') id: Cat['id']): Promise<Cat | undefined> {
    const result = await this.catsService.findOne(id);

    return result ? result : { message: "渡されたIDを持つネコはいませんでした" };
  }
}

そして、先ほどと同じように/cats/lastに対してGETリクエストを投げると、
無事思い通りの挙動をした😌

/cats/lastを投げた時の結果
{
  "message": "まだ一匹も登録されていません" 
}

この事からControllerのメソッドの順番によって、ルーティングの挙動が変わることが分かった。
めでたしめでたし👏

参考

公式ドキュメントで一応探したけど全然記述が見つからなかったので、
githubのissuesの方を参照させてもらいます。

https://github.com/nestjs/nest/issues/995#issuecomment-415712163

uttkuttk

テストについて

あとで書く...🖊

メモ

  • 徹底的に調べる
  • ここら辺の情報が全く無いので、確実に記事にすること!
  • いい加減、ts-jestbaseUrlpaths 問題が鬱陶しいので、ついでに解説する
uttkuttk

シード値を扱う方法について

色々とやったけど、以下の記事のやり方がベストな気がした。
暇が出来たら、Seedを扱えるようにするライブラリを書こうかな🤔

https://github.com/typeorm/typeorm/issues/1550#issuecomment-381039669

uttkuttk

リレーションについて

あとで書く...🖊

uttkuttk

更新できてないので、クローズ。
半年後にプロダクトで使うかもしれないので、その時にまた追記するかもしれません。

このスクラップは2021/05/07にクローズされました