前回の記事でTypeORMを使ってテーブル作成まで実装できたので、簡単なブログ登録機能とバリデーションを実装していきます
↓ 前回までの実装内容
ゴール
- ブログに関するCRUD機能が実装できていること
参考リンク
開発環境
- VSCode
- 実行環境
- Node.js:
v16.x
- yarn:
v.1.22.x
- Node.js:
- ライブラリ
- @nestjs/cli:
8.2.x
- @nestjs/typeorm:
8.x
- typeorm:
0.2.x
- mysql2:
2.3.x
- @nestjs/cli:
導入手順
前提:プロジェクトフォルダが作成されている かつ テーブルが作成されていること
- blogs.moduleの作成
- blogs.controllerの作成
- blogs.serviceの作成
- 動作確認
- Repositoryの追加
- バリデーション(DTO)の追加
実装
現時点でのディレクトリ構成はコチラ
├── README.md
├── dist
├── docker-compose.yml #MySQL用
├── nest-cli.json
├── ormconfig.js
├── package.json
├── src
│ ├── app.module.ts
│ ├── blogs
│ │ ├── blog-status.enum.ts
│ ├── entities
│ │ └── blog.entity.ts
│ ├── main.ts
│ └── migrations
│ └── 1648475134277-CreateBlog.ts
├── test
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
blogs.moduleの作成
下記コマンドにてmoduleファイルを生成する
$ npx nest g module blogs
CREATE src/blogs/blogs.module.ts (82 bytes)
UPDATE src/app.module.ts (447 bytes)
app.moduleには自動でimportされる
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BlogsModule } from './blogs/blogs.module'; //追加
@Module({
imports: [TypeOrmModule.forRoot(), BlogsModule], //BlogsModuleが追加される
controllers: [],
providers: [],
})
export class AppModule {}
blogs.moduleは下記のように実装
import { TypeOrmModule } from '@nestjs/typeorm';
import { Module } from '@nestjs/common';
@Module({
imports: [TypeOrmModule.forFeature()],
providers: [],
controllers: [],
})
export class BlogsModule {}
後ほどBlogsRepositoryを作成し、それをBlogsServiceから呼び出す形になりますので、
forFeatureの引数は一旦空にしておきます。
blogs.controller&blogs.serviceの作成
下記コマンドにてcontrollerファイルを生成する
$ npx nest g controller blogs --no-spec
CREATE src/blogs/blogs.controller.ts (99 bytes)
UPDATE src/blogs/blogs.module.ts (333 bytes)
$ npx nest g service blogs --no-spec
CREATE src/blogs/blogs.service.ts (89 bytes)
UPDATE src/blogs/blogs.module.ts (393 bytes)
※app.moduleのcontrollersには自動で追加される
blogs.controllerを以下のように実装
import { Controller, Post } from '@nestjs/common';
import { BlogsService } from './blogs.service';
@Controller('blogs')
export class BlogsController {
constructor(private readonly blogsService: BlogsService) {}
@Post()
create() {
return this.blogsService.create();
}
}
※blogを実際にcreateする処理はBlogsServiceに実装
blogs.serviceは以下のように実装
import { Injectable } from '@nestjs/common';
@Injectable()
export class BlogsService {
create() {
return 'success create blog!!';
}
}
※ 一旦適当な文字列を返すのみで実装
※ 最終的にはBlogsRepositoryを使ってDBとの疎通を行う
動作確認
一旦現時点の状態で疎通がうまくいくか確認するため、まずはサーバー起動
$ yarn start:dev
先程のBlogsControllerは/blogsパスかつPOSTメソッドに設定しているため、下記コマンドでリクエストを投げる
$ curl -X POST localhost:3000/blogs
success create blog!!
これで無事にblogsモジュールが正しくDIされて、目的の処理が行われていることが確認できました
これからDBとの疎通をするためにBlogsRepositoryを作成していきます
Repositoryの追加
blogs.repositoryを作成して以下のように実装
import { Blog } from '../entities/blog.entity';
import { EntityRepository, Repository } from 'typeorm';
import { BlogStatus } from './blog-status.enum';
@EntityRepository(Blog)
export class BlogsRepository extends Repository<Blog> {
async createBlog(): Promise<Blog> {
const Blog = this.create({
title: 'test-title',
content: 'test-content',
status: BlogStatus.PUBLISH,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
await this.save(Blog);
return Blog;
}
}
※登録する値は一旦仮値で登録します
blogs.service内でrepositoryからDBへcreateするように修正
import { Injectable } from '@nestjs/common';
import { BlogsRepository } from './blogs.repository'; //追加
@Injectable()
export class BlogsService {
constructor(private readonly blogsRepository: BlogsRepository) {} //追加
create() {
return this.blogsRepository.createBlog(); //変更
}
}
この状態でサーバーを起動し、実際にリクエストを投げてみる
$ curl -X POST -H "Content-Type:application/json" -d '{"title": "test-title", "content":"test-content"}' localhost:3000/blogs
{"title":"test-title","content":"test-content","status":1,"createdAt":"2022-04-02T14:19:29.438Z","updatedAt":"2022-04-02T14:19:29.438Z","id":1}
正常にDBへ登録できたことを確認できた
バリデーション追加
次にリクエストBodyに対して、バリデーションを適用してみます
バリデーションはNestJSの機能であるPipesを利用していきます
参考:https://docs.nestjs.com/techniques/validation#validation
まずはバリデーションに必要となるライブラリを追加
$ npm i -s class-transformer class-validator
create-blog.dto.tsを作成し、以下のように実装
import { IsString, IsNotEmpty, MaxLength, IsOptional } from 'class-validator';
export class CreateBlogDto {
@IsString()
@IsNotEmpty()
@MaxLength(50)
title: string;
@IsString()
@IsOptional() //パラメータが存在した時のみチェック
content: string;
}
controller, service, repositoryそれぞれの引数にCreateBlogDtoが型である変数を入れる
//controller
@Post()
create(@Body() createBlogDto: CreateBlogDto) {
return this.blogsService.create(createBlogDto);
}
//service
create(createBlogDto: CreateBlogDto) {
return this.blogsRepository.createBlog(createBlogDto);
}
//repository
@EntityRepository(Blog)
export class BlogsRepository extends Repository<Blog> {
async createBlog(createBlogDto: CreateBlogDto): Promise<Blog> {
const { title, content } = createBlogDto;
const Blog = this.create({
title,
content,
status: BlogStatus.PUBLISH,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
await this.save(Blog);
return Blog;
}
}
最後にmain.tsにバリデーションが適用されるようにグローバルスコープ付きパイプとして設定する
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()); //追加
await app.listen(3000);
}
bootstrap();
これでバリデーションの適用が完了したため、動作を確認してみる
#登録されるパターン
$ curl -X POST -H "Content-Type:application/json" -d '{"title": "title_1", "content":"content_1"}' localhost:3000/blogs
{"title":"title_1","content":"content_1","status":1,"createdAt":"2022-04-07T14:06:07.860Z","updatedAt":"2022-04-07T14:06:07.860Z","id":4}%
#エラーになるパターン titleを数字でリクエスト
$ curl -X POST -H "Content-Type:application/json" -d '{"title": 999999, "content":"content_2"}' localhost:3000/blogs
{"statusCode":400,"message":["title must be shorter than or equal to 50 characters","title must be a string"],"error":"Bad Request"}%
しっかりとバリデーションが適用されていること、そして値が正常の場合はDBに登録されていることを確認できた。
さいごに
前回作成したblogsテーブルに登録する処理を実装することができました。
デコレータを使ってバリデーションを実装できるのは非常に便利でした。
次は、認証や他の機能も実装していきたいと思います。
ご覧頂きありがとうございました。