NestJSの知見まとめ
@nestjs/configについて
どんなライブラリ?
NestJSアプリで環境変数を扱えるようになります。
使い方
ConfigModule.forRoot()
をインポートする
環境変数を使いたいModuleクラスで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クラスで使う方法
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 {}
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") ); // 環境変数を読み込んで使う
}
}
オプションについて
公式リポジトリの情報を参考にしています。
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 {}
ConfigService
について初めて知りました!👀
まだまだ勉強中の身なのですが、僕も下記で知見をまとめたので何かのご参考になれたら嬉しいなと思いました🙋♂️
引き続きウォッチしながら NestJS で機能作成する際の参考にさせていただきたいと思います📝
まだまだ勉強中の身なのですが、僕も下記で知見をまとめたので何かのご参考になれたら嬉しいなと思いました🙋♂️
おお!
NestJSは日本語の情報が全然無いので、とても助かります😭
後でじっくり読んで参考にさせて頂きますね。
引き続きウォッチしながら NestJS で機能作成する際の参考にさせていただきたいと思います📝
ありがとうございます!
今後も知見を得たら追加していく予定なので、是非参考にしてご自身の開発に役立てて下さい!
ある程度まとまったら記事するつもりなので、より詳細な解説はそちらの方でするかもです💁♂️
なので、このスクラップは気軽に見てくれると嬉しいです😆
NestJSは日本語の情報が全然無いので、
情報共有し合って、より良い開発につなげられていけば良いなと思っています💪
こちらこそありがとうございます!☺️
NestJSは日本語の情報が全然無いので、
情報共有し合って、より良い開発につなげられていけば良いなと思っています💪
同じく NestJS の日本語情報少ないなと思っていたので @uttk さんの知見は非常にありがたいです!📝
おかげさまで知見アウトプットする自分のモチベにもなりました!💪
記事楽しみにしております!😆 (ワクワク
気になったこと
気になったことを追記していく...🖊
Serviceクラスのコンストラクタの引数について
事の発端
Serviceクラスのコンストラクタ引数の順番を逆にしてもちゃんと動いたので、
「 なんでだろう?🤔」ってなった。
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 standardtsc
compiler (for standard projects) or the webpack compiler (for monorepos). It does not add any other compilation features or steps except for handlingtsconfig-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.
要約すると、
nestjsはtscコンパイラやwebpackコンパイラをラップしたコンパイラを使っている。
なので、Serviceクラスから別のServiceクラスをコンストラクタで受け取る時、型を見てコンストラクタに渡す値を判断している模様( タブン )。
そのため、引数の順番はTypeScriptで型をつけてれば関係ない。
はぇー、NestJS凄い🙄
Controllerでメソッドを書くときは順番には気をつけよう!って話
結論
結論だけ知りたい人に向けて、先に結論言っておきます。
- Controllerのメソッドの順番にはちゃんと意味があるよ
- メソッドの順番はExpressによるものだよ
- パラメーターを持つメソッドはなるべく下の方に記述したほうがいいよ
- 公式ドキュメントだけじゃなくて、issuesとかも読んでおいた方がいいよ
本題
以下のようなControllerクラスを書いた。
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
の方のメソッドが実行される。
{
"message": "渡されたIDを持つネコはいませんでした"
}
この時、 /cats/:id
の :id 部分に /cats/last
の last の部分が引っかかってしまっているのだろうと予測できるが、/cats/all
は普通に実行できているので、「 これはおかしい…🤔」となる。
そこで、CatsControllerの getLast()
を findOne()
よりも先に書くように修正した。
/* -- 省略 -- */
@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
リクエストを投げると、
無事思い通りの挙動をした😌
{
"message": "まだ一匹も登録されていません"
}
この事からControllerのメソッドの順番によって、ルーティングの挙動が変わることが分かった。
めでたしめでたし👏
参考
公式ドキュメントで一応探したけど全然記述が見つからなかったので、
githubのissuesの方を参照させてもらいます。
ステージングについて
記事を書きましたので、そちらを参考にして下さい🔭
セッション管理について
あとで書く...🖊
バリデーションについて
あとで書く...🖊
テストについて
あとで書く...🖊
メモ
- 徹底的に調べる
- ここら辺の情報が全く無いので、確実に記事にすること!
- いい加減、
ts-jest
のbaseUrl
・paths
問題が鬱陶しいので、ついでに解説する
後で参考にするURLを列挙
- DBを用いたテストについて
- 大規模なデータのインポートについて
- Soft Deleteについて
NestJS + NextJSを使う例
後で調べる。
更新できてないので、クローズ。
半年後にプロダクトで使うかもしれないので、その時にまた追記するかもしれません。