はじめてのnest new

6 min read読了の目安(約5600字

目的

仕事でNestJSを使うのですが自分でnest newしたことなかったので学習の意味で簡単なCRUD処理を作成したので、記録と残して置こうと思いキーボードを執りました。

環境

OS:macOS Big Sur 11.1
Node:v14.4.0
NestJS:7.5.4

導入

npm i -g @nestjs/cli

新規プロジェクト作成

nest new nestjs_todo

必要なものを導入

npm i @nestjs/typeorm typeorm mysql2 class-transformer class-validator

ormconfig.jsonを作成

ormconfig.json
{
  "name": "default",
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "",
  "database": "nest",
  "synchronize": true,
  "logging": true,
  "entities": [
    "dist/entities/**/*.entity.js"
  ],
  "migrations": [
    "dist/migrations/**/*.js"
  ]
}

エンティティファイル作成

src/entities/task.entity.ts
import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from "typeorm";

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  readonly id: number;

  @Column()
  title: string;

  @CreateDateColumn()
  readonly createdAt?: Date;

  @UpdateDateColumn()
  readonly updatedAt?: Date;
}

migrationファイル作成

npx typeorm migration:generate -d src/migrations -n create-task
src/migrations/1608641968685-create-task.ts
import {MigrationInterface, QueryRunner} from "typeorm";

export class createTask1608641968685 implements MigrationInterface {
    name = 'createTask1608641968685'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query("CREATE TABLE `task` (`id` int NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `createdAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `updatedAt` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`)) ENGINE=InnoDB");
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query("DROP TABLE `task`");
    }

}

migration実行

npx typeorm migration:run

Controller作成

nest g controller tasks
src/tasks/tasks.controller.ts
import { Controller } from '@nestjs/common';

@Controller('tasks')
export class TasksController {}

Service作成

nest g service tasks
src/tasks/tasks.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class TasksService {}

モジュール作成

nest g module tasks
src/tasks/tasks.module.ts
import { Module } from '@nestjs/common';

@Module({})
export class TasksModule {}

Read処理を実装

src/tasks/tasks.controller.ts
import { Controller, Get } from '@nestjs/common';
import { Task } from 'src/entities/task.entity';
import { TasksService } from './tasks.service'

@Controller('tasks')
export class TasksController {
  constructor(private readonly taskService: TasksService) {}

  @Get()
  async findAll(): Promise<Task[]> {
    return await this.taskService.findAll()
  }
}
src/tasks/tasks.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Task } from 'src/entities/task.entity';
import { TasksController } from './tasks.controller';
import { TasksService } from './tasks.service';

@Module({
  controllers: [TasksController],
  imports: [TypeOrmModule.forFeature([Task])],
  providers: [TasksService],
  exports: [TypeOrmModule],
})
export class TasksModule {}
src/tasks/tasks.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Task } from 'src/entities/task.entity';
import { Repository } from 'typeorm';

@Injectable()
export class TasksService {
  constructor(
    @InjectRepository(Task)
    private readonly taskRepository: Repository<Task>
  ) {}

  async findAll(): Promise<Task[]> {
    return await this.taskRepository.find()
  }
}

Create処理を実装

src/dto/create-task.dto.ts
import { IsNotEmpty, IsString } from "class-validator";

export class CreateTaskDTO {
  @IsNotEmpty()
  @IsString()
  title: string;
}
src/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();
src/tasks/tasks.controller.ts
@Post()
async create(@Body() createTaskDto: CreateTaskDTO) {
  return await this.taskService.create(createTaskDto)
}
src/tasks/tasks.service.ts
async create(task: CreateTaskDTO): Promise<InsertResult> {
  return await this.taskRepository.insert(task)
}

Update処理を実装

src/dto/update-task.dto.ts
import { IsNotEmpty, IsString } from "class-validator";

export class UpdateTaskDto {
  @IsNotEmpty()
  @IsString()
  title: string;
}
src/tasks/tasks.controller.ts
@Put(':id')
async update(@Param('id') id: string, @Body() updateTaskDto: UpdateTaskDto) {
  return await this.taskService.update(+id, updateTaskDto)
}
src/tasks/tasks.service.ts
async update(id: number, updateTaskDto: UpdateTaskDto) {
  return await this.taskRepository.update(id, updateTaskDto)
}

Delete処理を実装

src/tasks/tasks.controller.ts
@Delete(':id')
async remove(@Param('id') id: string) {
  return this.taskService.remove(+id)
}
src/tasks/tasks.service.ts
async remove(id: number) {
  return await this.taskRepository.delete(id)
}

感想

ゼロから自分でやってみるというのはたいへんな部分もありましたが新たな知識を増やすことができました。そして、公式サイトのドキュメントのわかりやすさを再実感しました。
コード全体は下記のリポジトリで確認できるようにしてあります。

https://github.com/youheiyouhei/nestjs_todo

参考

https://docs.nestjs.com
https://github.com/typeorm/typeorm