背景
前回は純粋なTypeORM環境をCLIで構築してみましたが、TypeORMはNestというフレームワークのORMとして利用されているとのことを知ったので、今度はNestでTypeORM環境を構築してみようと思う。
https://qiita.com/yusuke-ka/items/195e6bba4f21a659b424
Nest環境の構築
前回同様、DBは以前インストールしたpostgresql(windows)を利用する。
https://qiita.com/yusuke-ka/items/448843020c0406363ba5#%E6%BA%96%E5%82%99
pgadmin4でデータベースインスタンスだけ作っておく。
データベース名("nest_typeorm"とした)を入力して作成。
ここからは、コード エディタ(VS Code)上での作業。
諸事情により、まずは作業用のnode環境を作る。
> mkdir work
> cd work
nestのインストールは通常は下記のコマンドで実施する。
(linux環境の場合は下記でやってしまってOK.)
yarn global add @nestjs/cli
本当は↑でインストールしたいところだけど、windowsだとそのままだと使えずパスを通してやる必要があり面倒なので、↓でインストール。
yarn add @nestjs/cli
続いて、nestで新しいプロジェクトを作る。
以下のようにすれば、今いるフォルダを直接プロジェクトのフォルダにできるけど、すでにpackage.jsonが存在していて怒られるので(globalインストールしていればpackage.jsonがないので実行できる)、今いるフォルダは作業用として、下にプロジェクトを作る。
> npx nest new .
↓今回はこっちで実行。
> npx nest new nest
npmかyarnかを聞かれるので、今回はyarnを選択。
yarnでnestコマンドが使えるように、作成されたnestプロジェクト以下のpackage.jsonにscriptを追加しておく。
(npxで実行するのであれば下記の修正は不要だけど、なんとなくyarnでやりたい。)
"scripts":{"nest":"nest",...},
typeORM関連の依存をインストールしておく。
> yarn add @nestjs/typeorm typeorm pg
最後に設定ファイル(app.module.ts)を更新。
import{Module}from'@nestjs/common';import{TypeOrmModule}from'@nestjs/typeorm';@Module({imports:[TypeOrmModule.forRoot({type:'postgres',host:'localhost',port:5432,username:'postgres',password:'postgres',database:'nest_typeorm',entities:[],synchronize:true,}),],controllers:[],providers:[],})exportclassAppModule{}
デフォルトで、AppControllerやAppServiceなどがimportされているが、これらは最初から入っているサンプルコードなので消してもよい。
今回は使わないので消しておいた。
ついでにsrc以下にある下記のファイルもいらないので削除。app.controller.ts
app.controller.spec.ts
app.service.ts
これでNest環境のベースは整った。
Nestでサンプル実装してみる
最初にCLIのジェネレータでコードのひな型を作っておく。
> yarn nest g service TestObject
> yarn nest g controller TestObject
> yarn nest g module TestObject
test-objectフォルダが作成され、その下に各ひな形が配置される。
さらに以下のコマンドでモデルのひな形も作成。
(こちらは、明示的にフォルダを指定しないと、「test-object」フォルダに入らなかったので、「test-object/TestObject」のように指定。)
> yarn nest g class test-object/TestObject
test-objectフォルダ以下は下のようになる。
|- src
|- test-object
|- test-object.ts
|- test-object.controller.ts
|- test-object.controller.spec.ts
|- test-object.module.ts
|- test-object.service.ts
|- test-object.service.spec.ts
|- test-object.spec.ts
~.spec.ts
が一緒に生成されるがテスト用のコードみたいなので、今回は触れない(今度試してみようと思う)。
また、ジェネレートコマンドを実行すると、app.module.tsが自動的に更新されてしまう。
生成したcontrollerやserviceが自動でimportされるみたい。
今回はモジュール化するためのファイルも作っていて、直接app.module.tsにはimportしなくてよいはずなので、以下のように修正しておく。
import{Module}from'@nestjs/common';import{TypeOrmModule}from'@nestjs/typeorm';import{TestObjectModule}from'./test-object/test-object.module';import{TestObject}from'./test-object/test-object';@Module({imports:[TypeOrmModule.forRoot({type:'postgres',host:'localhost',port:5432,username:'postgres',password:'postgres',database:'nest_typeorm',entities:[TestObject],synchronize:true,}),TestObjectModule,],controllers:[],providers:[],})exportclassAppModule{}
controllerやserviceは直接ここには書かずに、moduleだけを指定する感じ。
ただ、TypeOrmModuleのentitiesには、モデルオブジェクトを直接書いておかないとコンパイル時にエラーになったので注意。
ここからは中身を実装していく。
とその前に、各ファイルの役割を確認しておく。
サンプルのコードとかを見る限り、なんとなくこんな(↓)感じのイメージかな。
test-object.ts
: モデルを定義するファイルtest-object.service.ts
: TypeORMの機能を直接使ってDBを操作するファイルtest-object.controller.ts
: APIを定義するファイル。serviceの提供するメソッドを呼び出す。test-object.module.ts
: 定義したserviceとかcontrollerとかを一つのモジュールにして提供するファイル。
ということで、まずはモデルファイルを実装。
import{Entity,PrimaryGeneratedColumn,Column}from'typeorm';@Entity()exportclassTestObject{@PrimaryGeneratedColumn()id:number;@Column()attr1:string;}
シンプルに属性はidとattr1だけ。
続いてサービスの実装。
import{Injectable}from'@nestjs/common';import{InjectRepository}from'@nestjs/typeorm';import{Repository}from'typeorm';import{TestObject}from'./test-object';@Injectable()exportclassTestObjectService{constructor(@InjectRepository(TestObject)privatereadonlyrepository:Repository<TestObject>,){}asyncall():Promise<TestObject[]>{returnthis.repository.find();}asyncone(id:number):Promise<TestObject>{returnthis.repository.findOne(id);}asynccreate(data:Partial<TestObject>):Promise<TestObject>{returnthis.repository.save(data);}asyncupdate(id:number,data:Partial<TestObject>):Promise<void>{constorigin=awaitthis.repository.findOne(id);constupdateData=Object.assign(origin,data);// 上書きthis.repository.save(updateData);}asyncremove(id:number):Promise<void>{constobj=awaitthis.repository.findOne(id);this.repository.remove(obj);}}
こちらもシンプルなCRUDだけを実装してみました。
(read系は全件取得と単独指定で取得の2つ)
次は、このサービスを呼び出すコントローラの実装。
import{Controller,Get,Post,Param,Body,Delete,HttpCode,HttpStatus,Put,}from'@nestjs/common';import{TestObjectService}from'./test-object.service';import{CreateTestDataDTO,UpdateTestDataDTO}from'./test-object.dto';import{TestObject}from'./test-object';@Controller('test-object')exportclassTestObjectController{constructor(privatereadonlyservice:TestObjectService){}@Get()@HttpCode(HttpStatus.OK)all():Promise<TestObject[]>{returnthis.service.all();}@Get(':id')@HttpCode(HttpStatus.OK)one(@Param('id')id:number):Promise<TestObject>{returnthis.service.one(id);}@Post('create')@HttpCode(HttpStatus.CREATED)asynccreate(@Body()createTestDataDto:CreateTestDataDTO,):Promise<TestObject>{returnthis.service.create(createTestDataDto);}@Put('update/:id')@HttpCode(HttpStatus.NO_CONTENT)asyncupdate(@Param('id')id:number,@Body()updateTestDataDto:UpdateTestDataDTO,):Promise<void>{this.service.update(id,updateTestDataDto);}@Delete(':id')@HttpCode(HttpStatus.NO_CONTENT)asyncremove(@Param('id')id:number):Promise<void>{this.service.remove(id);}}
こちらもCRUD操作のAPIを定義。HTTPリクエストをここで処理している感じですね。
更新系の操作は引数にData Transfer Object(DTO)を渡している、これは、src/test-objectの下にtest-object.dto.tsとして別途定義した。
exportclassCreateTestDataDTO{attr1:string;}exportclassUpdateTestDataDTO{attr1:string;}
最後に、serviceやcontrollerをモジュール化するファイルの実装。
import{Module}from'@nestjs/common';import{TypeOrmModule}from'@nestjs/typeorm';import{TestObjectService}from'./test-object.service';import{TestObjectController}from'./test-object.controller';import{TestObject}from'./test-object';@Module({imports:[TypeOrmModule.forFeature([TestObject])],exports:[TypeOrmModule],providers:[TestObjectService],controllers:[TestObjectController],})exportclassTestObjectModule{}
このモジュールを大元のapp.module.tsでimportする感じになっている。
実行
起動して確認してみる。
> yarn start:dev
確認はいつもの「Advanced REST client」。
http://localhost:3000/test-objectに各種リクエストを送ってみる。
説明は省略(全部問題なく動きました。)。
作成
全件取得
更新
1件取得
削除
さいごに
今回はNestでTypeORM環境を構築してサンプル実装してみた。
前回、素のTypeORM環境をCLIで作成したときよりは時間がかかったけど、構造がなんとなく理解できたら、すんなりとコードを書くことができた。
そこそこの規模のコードを書くなら、こういったフレームワークを利用して書いた方が、コードを整理できて良さそうですね。テストのフレームワークも付いてるみたいだし。