Table of Contents
開発環境セットアップ
まず、Dockerを使って簡単に開発環境を構築します。
Dockerfileは以下のようになりました。
FROM node:12-alpine
RUN npm install -g @nestjs/cli@6.5
RUN apk update && apk add git
RUN mkdir workspace
WORKDIR /workspace
そして、docker-compose.ymlは以下のようになります。
version: "3"
services:
nest-app-web:
build: .
ports:
- 3040:3000
volumes:
- ./:/workspace/
links:
- nest-app-db
tty: true
nest-app-db:
image: mysql:5.7
volumes:
- mysql-data
environment:
MYSQL_ROOT_PASSWORD: "root"
MYSQL_USER: "root"
MYSQL_PASSWORD: "root"
MYSQL_DATABASE: "root"
ports:
- 13306:3306
volumes:
mysql-data:
その後コンテナを立ち上げます。
$ docker-compose up -d
プロジェクト作成
コンテナが起動したら、以下コマンドでプロジェクト作成、必要なパッケージのインストールを行います。
すると、 npm run start
コマンドで開発サーバーが起動できると思います。
開発の効率化のためにHMRを有効にするには前回の記事 が参考になります。
$ docker-compose exec nest-app-web nest new .
$ docker-compose exec npm install
$ docker-compose exec npm run start
上手くいけば、localhost:3040へアクセスでき、"Hello World!" と表示されるはずです。
TypeORM導入
$ docker-compose exec nest-app-web npm install @nestjs/typeorm typeorm mysql
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_USERNAME',
password: 'MY_PASSWORD',
database: 'MY_DATABASE',
entities: [join(__dirname, '**/**.entity{.ts,.js}')],
synchronize: true,
keepConnectionAlive: true,
})], // <- 追加
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ormconfig.jsonを作成して接続情報を記述すれば forRoot()
内は省略できるそうですが、syntax errorが出たので、直接記述しています。
リポジトリパターンの実装
モジュールの作成
Angularを知っている人はわかると思いますが、NestJSはモジュールという単位でアプリケーションが区切られてルートモジュール(app.module.ts)を元に紐づいていきます。
参考: https://docs.nestjs.com/modules
$ docker-compose exec nest-app-web nest g mo Posts
posts/posts.module.ts
import { Module } from '@nestjs/common';
@Module({})
export class PostsModule {}
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [TypeOrmModule.forRoot(), PostsModule], // <- 追加される
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
エンティティクラスの作成
エンティティクラスはデータベース直接紐づくクラスで、ここで定義したものがそのままデータベースのテーブルになります。
TypeORMのsynchronize
をtrueにすると、実行時に自動でテーブルが作成されます。
エンティティは残念ながら生成コマンドは無いようなので、自分でファイルを作成します。
posts/post.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 500 })
title: string;
@Column('text')
body: string;
}
posts.module.tsにエンティティを設定します。
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Post } from './post.entity';
@Module({
imports: [TypeOrmModule.forFeature([Post])], // <- 追加する
})
export class PostsModule {}
サービスクラスの作成
ビジネスロジックを担うサービスクラスですが、NestJSではProviderという概念で扱われ依存性注入(Dependency Injection)によってクラス間を疎結合な関係を保つことができます。
$ docker-compose exec nest-app-web nest g s Posts
posts/posts.module.ts
import { Module } from '@nestjs/common';
import { PostsService } from './posts.service';
@Module({
providers: [PostsService], // <- 追加される
})
export class PostsModule {}
posts/posts.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class PostsService {}
生成されてサービスクラスを編集、@InjectRepository()
デコレータでエンティティクラスからリポジトリクラスを呼び出し、サービスクラスに対して依存注入して呼び出しています。
import { Injectable, Inject } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './post.entity';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private readonly postRepository: Repository<Post>,
) {}
async findAll(): Promise<Post[]> {
return await this.postRepository.find();
}
}
コントローラクラスの作成
次はリクエストとレスポンスをさばくコントローラクラスです。
$ docker-compose exec nest-app-web nest g co Posts
posts/posts.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Post } from './post.entity';
import { PostsService } from './posts.service';
import { PostsController } from './posts.controller';
@Module({
imports: [TypeOrmModule.forFeature([Post])],
providers: [PostsService],
controllers: [PostsController], // <- 追加される
})
export class PostsModule {}
posts/posts.controller.ts
import { Controller } from '@nestjs/common';
@Controller('posts')
export class PostsController {}
先ほど用意したサービスクラスをコントローラクラスに依存注入して使い、Post一覧を返すメソッドを定義します。
import { Controller, Get } from '@nestjs/common';
import { PostsService } from './posts.service';
import { Post } from './post.entity';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll(): Promise<Post[]> {
return this.postsService.findAll();
}
}
すると、localhost:3040/posts へアクセスするとデータベースにあるレコード一覧のjsonが返ってきます。
最後に
TypeORMを利用することでデータベースと連携したNestJSアプリケーション開発ができるようになりました。
さらにモジュール、エンティティ、サービス、コントローラそれぞれのクラスを作成することで「関心の分離」を実現することができました。
さらにNestJSのCRUDパッケージ(@nestjsx/crud)と組み合わせることで簡単に CRUDを構築 することができるようですので試してみたいと思います。
Related Posts
Daiki Urata
2023/05/22
Daiki Urata
2023/01/05
Daiki Urata
2022/08/29