13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【NestJS】NestJSを使用してCRUD実装!

Last updated at Posted at 2022-09-19

logo-small.png

NestJSとは

NestJSとは、NodeJS環境で動くサーバーサイドアプリケーションフレームワークです。

基本的にTypeScriptでコーディングを行いますが、JavaScriptで書くことも可能です。

Expressをコアに作られているので、Expressの機能は備えつつ、テストフレームワークJestを標準装備していたり、NestCLIを利用して効率良く開発を進めることができたり、多くの機能を搭載しています。

今回は、DockerでPostgreSQLサーバを立ち上げ、NestJSを用いた商品管理のCRUD実装を行なっていこうと思います。

1. NestJSのプロジェクト作成

NestCLIを使用して、NestJSのプロジェクトを立ち上げます。

まずは以下コマンドで、NestCLIがインストールされているか確認します。

nest --version

バージョンが表示されれば、既にインストールされています。command not foundなどのエラーが出た場合はNestCLIがインストールされていないので、以下コマンドを実行してください。

npm i -g @nestjs/cli

インストールが完了したら、以下コマンドでNestJSのプロジェクトを立ち上げます。

nest new nest-tutorial

使用するパッケージマネージャーを選べますので、今回はnpmを選択してください

? Which package manager would you ❤️ to use? (Use arrow keys)
❯ npm 
  yarn

以下のようなログが出れば、NestJSプロジェクトの立ち上げ完了です!👏

                       Thanks for installing Nest 🙏
              Please consider donating to our open collective
                     to help us maintain this package.
                                         
                                         
               🍷  Donate: https://opencollective.com/nest

2. DB接続(PostgreSQL)

2-1. PostgreSQLのサーバー起動

今回はPostgreSQLを利用します。

Dockerを使用してDBサーバーを立ち上げましょう。

以下Dockerファイルをルートディレクトリに置いてください。

docker-compose.yml
version: '3.7'
services:

  postgres:
    image: postgres:14.2-alpine
    container_name: nest-postgres
    ports:
      - 5432:5432
    volumes:
      - ./docker/postgres/init.d:/docker-entrypoint-initdb.d
      - ./docker/postgres/pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
      POSTGRES_DB: postgres
    hostname: postgres
    restart: always
    user: root

  pgadmin:
    image: dpage/pgadmin4
    restart: always
    ports:
      - 81:80
    environment:
      PGADMIN_DEFAULT_EMAIL: test@test.com
      PGADMIN_DEFAULT_PASSWORD: password
    volumes:
      - ./docker/pgadmin:/var/lib/pgadmin
    depends_on:
      - postgres

以下コマンドでPostgreSQLのコンテナを立ち上げます。

docker-compose up --build

コンテナが立ち上がったら、ブラウザでlocalhost:81にアクセスしてください。
以下のような画面が表示されれば、正常にPostgreSQLコンテナが立ち上がっています。
スクリーンショット 2022-09-18 15.40.37.png

メールアドレスとパスワードはそれぞれDockerファイルに記載されてあるPGADMIN_DEFAULT_EMAILPGADMIN_DEFAULT_PASSWORDの値を入力してください。

ログインできたら、DashbordからAdd New Serverを選択し、それぞれ以下の情報を入力してください。

項目 入力内容
Name nest-js-tutorial
Host name/address postgres
Port postgres
Maintenance database postgres
Username postgres
Password postgres

Saveボタンを押して、以下のような画面が表示されたら、PostgreSQLサーバーの立ち上げが完了

スクリーンショット 2022-09-19 14.50.42.png

2-2. ORM設定

では、立ち上げたPostgreSQLサーバーと、NestJSを接続していきましょう。

今回は、TypeORMというORMを使用します。

以下コマンドで、TypeORM、PostgreSQLのドライバーをインストールしてください。

npm i --save typeorm@0.2.45 @nestjs/typeorm@8.0.2 pg

ルートモジュール(/src/app.module.ts)を以下のように編集します。
TypeORMのモジュールをインポートし、forRoot()メソッドをimportsの中に入れます。

src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    TypeOrmModule.forRoot()
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

ルートディレクトリにormconfig.jsを作成し、以下のようなファイルを作成してください。
TypeORMはこのファイルを元にDB接続を行ってくれます。

ormconfig.js
module.exports = {
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  username: 'postgres',
  password: 'postgres',
  database: 'postgres',
  autoLoadEntities: true,
  entities: ['dist/entities/*.entity.js'],
  migrations: ['dist/migrations/*.js'],
  cli: {
    entitiesDir: 'src/entities',
    migrationsDir: 'src/migrations',
  },
};

では、以下のNestCLIコマンドを実行して、実際にNestJSサーバーを立ち上げてみましょう。

npm run start:dev

以下なログが出力されて、エラーログっぽいものがでなければDB接続が完了です!
スクリーンショット 2022-09-18 21.40.20.png

3. エンティティの作成

エンティティとは、データベースのテーブル毎に作成されるテーブル定義をするものです。

typeormモジュールからEntityデコレーターをインポートして、classに付与することでエンティティクラスを作成することができます。

クラス内で、それぞれtypeormモジュールからインポートしたデコレーターを使用して、カラム毎のルールを定義していきます。

src/entities/item.entity.ts
import { PrimaryGeneratedColumn, Column, Entity } from 'typeorm';

@Entity()
export class Item {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;

  @Column()
  price: number;

  @Column()
  createdAt: string;

  @Column()
  updatedAt: string;
}

4. マイグレーション実行

マイグレーションをおこなっていきます。
以下のTypeORMのCLIコマンドを使用して、マイグレーションファイルを作成します。

npx typeorm migration:generate -n CreateItemsTable -d src/migrations

実行すると、src/migrationsディレクトリ配下に、以下のようなファイルが生成されます。

src/migrations/1663567364289-CreateItemsTable.ts
import {MigrationInterface, QueryRunner} from "typeorm";

export class CreateItemsTable1663567364289 implements MigrationInterface {
    name = 'CreateItemsTable1663567364289'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE "items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "price" integer NOT NULL, "createdAt" character varying NOT NULL, "updatedAt" character varying NOT NULL, CONSTRAINT "PK_ba5885359424c15ca6b9e79bcf6" PRIMARY KEY ("id"))`);
    }

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

}

他バックエンドのフレームワークに詳しい方は、これからマイグレーションファイルにテーブル定義を記載していくと思われる方も多いかと思いますが、TypeORMでは、紐付くエンティティを見てマイグレーションを行なってくれるので、マイグレーションファイル自体を編集することは基本的にありません。

では、実際にマイグレーションを実行しましょう。

npx typeorm migration:run

以下のようなログが出たら、マイグレーション成功です!

query: SELECT * FROM current_schema()
query: CREATE EXTENSION IF NOT EXISTS "uuid-ossp"
query: SHOW server_version;
query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = 'public' AND "table_name" = 'migrations'
query: CREATE TABLE "migrations" ("id" SERIAL NOT NULL, "timestamp" bigint NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY ("id"))
query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = 'public' AND "table_name" = 'typeorm_metadata'
query: CREATE TABLE "typeorm_metadata" ("type" character varying NOT NULL, "database" character varying, "schema" character varying, "table" character varying, "name" character varying, "value" text)
query: SELECT * FROM "migrations" "migrations" ORDER BY "id" DESC
0 migrations are already loaded in the database.
1 migrations were found in the source code.
1 migrations are new migrations that needs to be executed.
query: START TRANSACTION
query: CREATE TABLE "items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "price" integer NOT NULL, "createdAt" character varying NOT NULL, "updatedAt" character varying NOT NULL, CONSTRAINT "PK_ba5885359424c15ca6b9e79bcf6" PRIMARY KEY ("id"))
query: INSERT INTO "migrations"("timestamp", "name") VALUES ($1, $2) -- PARAMETERS: [1663567364289,"CreateItemsTable1663567364289"]
Migration CreateItemsTable1663567364289 has been executed successfully.
query: COMMIT

テーブルが実際に作成されているか、localhost:81にアクセスし、データベースを見てみましょう

横のタブから、postgres👉Databases(1)👉Schemas(1)👉Tables(3)の順に開くと、データベースのテーブルを見ることができます。

スクリーンショット 2022-09-19 15.05.05.png

itemsテーブルががありますね!

マイグレーションが無事に成功しました!👏

5. モジュール/コントローラー/サービスの作成

NestJSでは、モジュールサービスコントローラーの3つで、1つの機能を実装していきます。

5-1. サービス作成

NestCLIコマンドでサービスを作成します。

NestCLIを利用してサービス/コントローラーを作成すると、テストファイルを自動生成してくれます。
今回はテストファイルは必要ないので、--no-specオプションを付与して自動生成を無効にします。

nest generate service items --no-spec

srcディレクトリ配下に、itemsディレクトリが自動で作成され、その配下にitems.service.tsが作成されます。

src/items/items.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class ItemsService {}

5-2. コントローラー作成

NestCLIコマンドでコントローラーを作成します。

nest generate controller items --no-spec

itemsディレクトリ配下にitems.controller.tsが作成されます。
コンストラクターで、サービスクラスをDIしておきます。

src/items/items.controller.ts
import { Controller } from '@nestjs/common';
import { ItemsService } from './items.service';

@Controller('items')
export class ItemsController {
  constructor (private readonly itemsService: ItemsService) {}
}

5-3. モジュール作成

NestCLIコマンドでモジュールを作成します。

nest generate module items

itemsディレクトリ配下にitems.module.tsが作成されます。

src/items/items.module.ts
import { Module } from '@nestjs/common';
import { ItemsController } from './items.controller';
import { ItemsService } from './items.service';

@Module({
  controllers: [ItemsController],
  providers: [ItemsService]
})
export class ItemsModule {}

5-4. ルートモジュールに登録

ルートモジュール(app.module.ts)に登録することで追加した機能が使えるようになります。
src/app.module.tsで作成したitemsモジュールをインポートして、importsにモジュールクラス追加してください。

src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
// モジュールをインポート!
import { ItemsModule } from './items/items.module';

@Module({
  imports: [
    TypeOrmModule.forRoot(),
    // 追加!!
    ItemsModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

これにて、itemsモジュールの機能が使えるようになりました👏

6. リポジトリの作成

6-1. リポジトリクラスの作成

リポジトリクラスとは、DB操作を実際に行うメソッド群を持つクラスです。

src/itemsディレクトリ配下に、items.repository.tsファイルを作成して、以下のように編集します。

src/items/items.repository.ts
import { Items } from 'src/entities/items.entity';
import { EntityRepository, Repository } from 'typeorm';

@EntityRepository(Items)
export class ItemsRepository extends Repository<Items> {}

@EntityRepository()デコレーターをインポートし、引数に紐付くエンティティを渡します。

Repositoryクラスを継承し、TypeORMのメソッド群を使用できるようにし、ジェネリクスに紐付くエンティティを設定します。

6-2. モジュールにリポジトリを登録

モジュール(items.module.ts)にリポジトリクラスを登録します。

importsプロパティにTypeOrmModule.forFeature()メソッドを入れます。
forFeatureの引数に作成したリポジトリクラスを含む配列にを入れます。

これでサービスでリポジトリをDIすることが可能になりました。

src/items/items.module.ts
import { Module } from '@nestjs/common';
// 追加=============================================
import { TypeOrmModule } from '@nestjs/typeorm';
import { ItemsRepository } from './items.repository';
// ↑↑↑↑============================================
import { ItemsController } from './items.controller';
import { ItemsService } from './items.service';

@Module({
  // 追加=============================================
  imports: [TypeOrmModule.forFeature([
    ItemsRepository
  ])],
  // ↑↑↑↑============================================
  controllers: [ItemsController],
  providers: [ItemsService]
})
export class ItemsModule {}

6-3. サービスでリポジトリクラスをDI

サービスクラス内でリポジトメソッドを使用できるように、コンストラクタでリポジトリをDIします。

src/items/items.service.ts
import { Injectable } from '@nestjs/common';
// 追加=============================================
import { ItemsRepository } from './items.repository';
// ↑↑↑↑============================================

@Injectable()
export class ItemsService {
  // 追加=============================================
  constructor (private readonly itemsRepository: ItemsRepository) {}
  // ↑↑↑↑============================================
}

7. DTOの作成

DTOを定義していきます。

DTOとはプロパティ毎の型などを定義したものを集めたオブジェクトで、登録や更新処理を行う際に使います。

更に便利なのが、型定義と同時にバリデーションルールも設定することができます。

バリデーションルールを設定するために、以下のパッケージclass-validatorをインストールしましょう。

npm i --save class-validator

src/itemsディレクトリにdtoディレクトリを作り、その配下にitem.dto.tsファイルを作成します。

それぞれのプロパティの上に、class-validatorからインポートしたデコレーターを使用してバリデーションルールを指定していきます。
こうすることで、リクエスト時にバリデーションルールに引っかかると、エラーが返るようになります。

src/items/dto/item.dto.ts
import { IsNumber, IsString } from 'class-validator';

export class ItemDto {
  @IsString() // 文字であること
  name: string;

  @IsNumber() // 数値であること
  price: number;
}

8. 登録機能の作成(CREATE)

8-1. リポジトリの編集

リポジトリに、登録処理を行うメソッドcreateItemを作成します。

TypeORMメソッドcreateで新たなデータオブジェクトを作成し、saveで実際にデータベースに保存しています。

データベース操作処理は非同期で行われる為、メソッドの返り値はPromise型にして、ジェネリクスで紐付くエンティティクラスを指定します。

src/items/item.repository.ts
import { EntityRepository, Repository } from 'typeorm';
// 追加=============================================
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
// ↑↑↑↑============================================

@EntityRepository(Items)
export class ItemsRepository extends Repository<Items> {
  // 追加=============================================
  async createItem (itemDto: ItemDto): Promise<Items> {
    const { name, price } = itemDto;

    const item = this.create({
      name,
      price,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString()
    }); // データオブジェクトを作成

    await this.save(item); // 実際にDBに登録

    return item; // 作成したデータを返す
  }
  // ↑↑↑↑============================================
}

8-2. サービスの編集

作成したリポジトリメソッドcreateItemを実行するメソッドを作成します。

引数には、作成したDTO型のオブジェクトを定義しましょう。

src/items/items.service.ts
import { Injectable } from '@nestjs/common';
import { ItemsRepository } from './items.repository';
// 追加=============================================
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
// ↑↑↑↑============================================

@Injectable()
export class ItemsService {
  constructor (private readonly itemsRepository: ItemsRepository) {}

  // 追加=============================================
  async createItem (itemDto: ItemDto): Promise<Items> {
    return await this.itemsRepository.createItem(itemDto);
  }
  // ↑↑↑↑============================================
}

8-3. コントローラーの編集

最後に、URL/itemsにPOSTリクエストがきた場合に、登録処理を行うようにハンドラーメソッドを作成しましょう。

引数は、@Body()を利用してリクエストボディにアクセスすることができます。

リクエストボディとして渡ってきたItemDto型のオブジェクトを引数にして、サービスメソッドcreateUserを実行しましょう。

src/items/items.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { ItemsService } from './items.service';
// 追加=============================================
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
// ↑↑↑↑============================================

@Controller('items')
export class ItemsController {
  constructor (private readonly itemsService: ItemsService) {}

  // 追加=============================================
  @Post() // @Post()デコレーターを使用して、POSTリクエストに対するハンドラーを登録
  // サービスのcreateItemメソッドを実行するハンドラを定義
  // DB操作は非同期処理のため、返り値の型は、Promise。ジェネリクスでItemsエンティティを付与
  async createUser (@Body() itemDto: ItemDto): Promise<Items> {
    return await this.itemsService.createItem(itemDto);
  }
  // ↑↑↑↑============================================
}

8-4. 動作確認

Postmanで、作成した登録APIの動作確認をしてみます。

メソッドをPOSTにし、URLをhttp://localhost:3000/itemsにします。

Bodyタブを選択し、x-www-form-unlencodedを選択します。

namepriceにそれぞれ値を指定し、Sendボタンを押してAPIを実行します。

以下のようなレスポンスが返って来れば、正常に登録処理が終了しています。

スクリーンショット 2022-09-19 19.04.32.png

データベースを見てみて、レスポンスとして返ってきたデータが登録されていることが確認できれば、登録APIの実装が完了です!👏
スクリーンショット 2022-09-19 19.08.32.png

9. 参照機能の作成(READ)

9-1. サービスの編集

全件取得と、一件取得のAPIを実装しましょう。

今回はTypeORMの標準メソッドのfindfindOneを実行すれば全件取得が可能なので、リポジトリの編集はしません。

TypeORMメソッドfind/findOneを実行するサービスメソッドをそれぞれ定義します。

全件取得の際は配列形式のレスポンスになる為、Promiseのジェネリクスで指定するエンティティクラスに[]を付与するのを忘れないでください。

src/items/items.service.ts
import { Injectable } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
import { ItemsRepository } from './items.repository';

@Injectable()
export class ItemsService {
  constructor (private readonly itemsRepository: ItemsRepository) {}

  // 追加=============================================
  // 全件取得
  async findAll (): Promise<Items[]> {
    return await this.itemsRepository.find();
  }

  // 1件取得
  async findById (id: string): Promise<Items> {
    // 引数として渡ってきたidを元に、データベースから検索
    const foundItem = await this.itemsRepository.findOne(id);
    if (!foundItem) {
      throw new NotFoundException(); // データが見つからない場合、例外を投げる
    }
    return foundItem;
  }
  // ↑↑↑↑============================================

  async createItem (itemDto: ItemDto): Promise<Items> {
    return await this.itemsRepository.createItem(itemDto);
  }
}

9-2. コントローラーの編集

@Get()デコレーターで、GETメソッドに対するハンドラーメソッドを作成します。

デコレーターの引数に文字列を付与することで、items/以降のパスを定義することができます。

:idとすることで、パスパラメータを定義することができます。

GETメソッドitems/でリクエストが来た場合は、サービスクラスのfindAllメソッドを実行するハンドラを定義し、

GETメソッドitems/:idでリクエストが来た場合は、サービスクラスのfindByIdメソッドを実行するハンドラを定義します。

src/items/items.controller.ts
import { Body, Controller, Get, Post } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemsService } from './items.service';
import { ItemDto } from './dto/item.dto'

@Controller('items')
export class ItemsController {
  constructor (private readonly itemsService: ItemsService) {}

  // 追加=============================================
  @Get()
  async findAll (): Promise<Items[]> {
    return await this.itemsService.findAll();
  }

  @Get(':id') // "items/"以降のパスを定義。パスパラメータにidを定義
  async findById (@Param() id: string): Promise<Items> {
    // パスパラメータのidを引数にして、サービスメソッドfindByIdを実行
    return await this.itemsService.findById(id);
  }
  // ↑↑↑↑============================================

  @Post()
  async createUser (@Body() itemDto: ItemDto): Promise<Items> {
    return await this.itemsService.createItem(itemDto);
  }
}

9-3. 動作確認

まずは全件取得のAPIのテストをしてみます。

メソッドをGETにして、URLをhttp://localhost:3000/itemsを実行してみます。

以下のように、配列形式で登録済みのデータが返れば、正常に全件取得処理が実行できています👏

スクリーンショット 2022-09-19 19.50.16.png

1件取得APIのテストをしてみます。

登録済みのデータのうちどれかのデータのidをコピーして、http://localhost:3000/items/の末尾にペーストして実行してください。

以下のように、オブジェクト形式でIDが一致する一件のデータが返れば、正常に1件取得処理が実行できています👏

スクリーンショット 2022-09-19 19.50.50.png

10. 更新機能の作成(UPDATE)

更新APIを実装しましょう。

流れとしては、パスパラメータのIDで取得したデータを、リクエストボディの値に更新するという処理になります。

10-1. サービスの編集

既存のfindByIdメソッドを利用して、IDからデータを検索します。

取得できたら、プロパティの値をリクエストボディの値に更新して、更新日を現在日時にします。

saveメソッドを実行してデータベースに反映します。

src/items/items.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
import { ItemsRepository } from './items.repository';

@Injectable()
export class ItemsService {
  constructor (private readonly itemsRepository: ItemsRepository) {}

  async findAll (): Promise<Items[]> {
    return await this.itemsRepository.find();
  }

  async findById (id: string): Promise<Items> {
    const foundItem = await this.itemsRepository.findOne(id);
    if (!foundItem) {
      throw new NotFoundException();
    }
    return foundItem;
  }

  async createItem (itemDto: ItemDto): Promise<Items> {
    return await this.itemsRepository.createItem(itemDto);
  }

  // 追加=============================================
  async updateItem (id: string, itemDto: ItemDto): Promise<Items> {
    let foudnItemToUpdate = await this.findById(id); // 更新するデータを取得
    foudnItemToUpdate.name = itemDto.name; // nameを更新
    foudnItemToUpdate.price = itemDto.price; // priceを更新
    foudnItemToUpdate.updatedAt = new Date().toISOString(); // 更新日時を更新
    return await this.itemsRepository.save(foudnItemToUpdate); // データ保存
  }
  // ↑↑↑↑============================================
}

10-2. コントローラーの編集

PATCHメソッドでリクエストを受けた際に、パラメータとリクエストボディを引数にしてサービスメソッドを実行します。

src/items/items.controller.ts
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemsService } from './items.service';
import { ItemDto } from './dto/item.dto'

@Controller('items')
export class ItemsController {
  constructor (private readonly itemsService: ItemsService) {}

  @Get()
  async findAll (): Promise<Items[]> {
    return await this.itemsService.findAll();
  }

  @Get(':id')
  async findById (@Param() id: string): Promise<Items> {
    return await this.itemsService.findById(id);
  }

  @Post()
  async createUser (@Body() itemDto: ItemDto): Promise<Items> {
    return await this.itemsService.createItem(itemDto);
  }

  // 追加=============================================
  @Patch(':id')
  async updateItem (
    @Param() id: string,
    @Body() itemDto: ItemDto
  ): Promise<Items> {
    return await this.itemsService.updateItem(id, itemDto);
  }
  // ↑↑↑↑============================================
}

10-3. 動作確認

更新したい既存のデータのidをコピーします。

スクリーンショット 2022-09-19 20.58.15.png

メソッドをPATCHにして、URLをhttp://localhost:3000/items/{コピーしたid}にします。

リクエストボディに、更新後の値を入れて、APIを実行します。

以下のように、更新後のデータがレスポンスとして返却されれば、APIが正常終了しています。

スクリーンショット 2022-09-19 20.59.43.png

メソッドをGETにして、URLをhttp://localhost:3000/items/{コピーしたid}で1件取得APIを実行し、データが更新されていることが確認できれば、更新APIの実装が完了です!👏
スクリーンショット 2022-09-19 21.02.21.png

11. 削除機能の作成(DELETE)

11-1. サービスの編集

TypeORMのdeleteメソッドを実行するサービスメソッドを作成します。

deleteメソッドの引数にIDを渡すことで、データの削除ができます。

レスポンスはないので、Promiseのジェネリクスにはvoidを設定します。

src/items/items.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemDto } from './dto/item.dto';
import { ItemsRepository } from './items.repository';

@Injectable()
export class ItemsService {
  constructor (private readonly itemsRepository: ItemsRepository) {}

  async findAll (): Promise<Items[]> {
    return await this.itemsRepository.find();
  }

  async findById (id: string): Promise<Items> {
    const foundItem = await this.itemsRepository.findOne(id);
    if (!foundItem) {
      throw new NotFoundException();
    }
    return foundItem;
  }

  async createItem (itemDto: ItemDto): Promise<Items> {
    return await this.itemsRepository.createItem(itemDto);
  }

  async updateItem (id: string, itemDto: ItemDto): Promise<Items> {
    let foudnItemToUpdate = await this.findById(id); // 更新するデータを取得
    foudnItemToUpdate.name = itemDto.name; // nameを更新
    foudnItemToUpdate.price = Number(itemDto.price); // priceを更新
    foudnItemToUpdate.updatedAt = new Date().toISOString(); // 更新日時を更新
    return await this.itemsRepository.save(foudnItemToUpdate); // データ保存
  }

  // 追加=============================================
  async deleteItem (id: string): Promise<void> {
    await this.itemsRepository.delete(id);
  }
  // ↑↑↑↑============================================
}

11-2. コントローラーの編集

DELETEメソッドでリクエストを受けた際に、パラメータとして渡ってきた商品IDを引数にして、作成したサービスメソッドdeleteItemを実行します。

src/items/items.controller.ts
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
import { Items } from 'src/entities/items.entity';
import { ItemsService } from './items.service';
import { ItemDto } from './dto/item.dto'

@Controller('items')
export class ItemsController {
  constructor (private readonly itemsService: ItemsService) {}

  @Get()
  async findAll (): Promise<Items[]> {
    return await this.itemsService.findAll();
  }

  @Get(':id')
  async findById (@Param() id: string): Promise<Items> {
    return await this.itemsService.findById(id);
  }

  @Post()
  async createUser (@Body() itemDto: ItemDto): Promise<Items> {
    return await this.itemsService.createItem(itemDto);
  }

  @Patch(':id')
  async updateItem (
    @Param() id: string,
    @Body() itemDto: ItemDto
  ): Promise<Items> {
    return await this.itemsService.updateItem(id, itemDto);
  }

  // 追加=============================================
  @Delete(':id')
  async deleteItem (@Param() id: string): Promise<void> {
    return await this.itemsService.deleteItem(id);
  }
  // ↑↑↑↑============================================
}

11-3. 動作確認

削除したい既存のデータのidをコピーします。

メソッドをDELETEにして、URLをhttp://localhost:3000/items/{コピーしたid}にして実行します。

エラーが出なければ、正常に削除処理が終了しています。

スクリーンショット 2022-09-19 21.09.59.png

メソッドをGETにして、URLをhttp://localhost:3000/items/{コピーしたid}で1件取得APIを実行し、404NotFoundエラーが返ってくれば、削除APIの実装が完了です!👏

スクリーンショット 2022-09-19 21.10.30.png

あとがき

これにてNestJSを使用したCRUD実装が完了です!!

認証機能の実装などの記事も今後書いてみたいと思っています。

この記事がNestJSの学習に少しでもお力になれたら幸いです!

最後まで読んでいただき、ありがとうございました!
rick-astley-never-gonna-give-you-up.gif

13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?