🤞 NomadcoderでNestjsの練習をする



レッスンリンクはこちらをクリックしてください👆


🧑 Nestjsを学ぶ

  • Nestjsは、Expressベースのサーバ開発フレームワークです.
  • Nestjsはとても怒って、私はずっと勉強したいです.
  • Expressでサーバを開発する場合は、無から有にする必要があります.
  • クールな仕事だと思いますが、私はいつも迷いと恐怖を感じています.
  • Java珍栄のSpringBootに似ていて、勉強したいという思いもあります.
  • PythonのDjangoのように、フォルダ名と基本設定が提供されています.
  • を使ってみると素晴らしい部分がたくさんあっていい感じです.
  • 👳‐‐‐‐‐‐‐‐‐Jestやってみる

  • を常に使用してテストライブラリを開発する必要があります...やっぱりずっと思ってた.
  • 今回の講座を通じて、私はその基本的な用途を学びました.
  • コードの更新はいつも怖いです.
  • 今は思い切り修正してテストすればいい!!(まだ足りないところはあるけど…)
  • 👱」フォルダ構造



    👮‍♂️ 合成コード

    // 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({
          whitelist: true,
          forbidNonWhitelisted: true,
          transform: true,
        }),
      );
      await app.listen(3000);
    }
    bootstrap();
    
    
    // app.module.ts
    import { Module } from '@nestjs/common';
    import { MoviesModule } from './movies/movies.module';
    import { AppController } from './app.controller';
    
    @Module({
      imports: [MoviesModule],
      controllers: [AppController],
      providers: [],
    })
    export class AppModule {}
    
    
    //app.controller.js
    import { Controller, Get } from '@nestjs/common';
    
    @Controller('')
    export class AppController {
      @Get()
      home() {
        return 'Welcome to my Movie API';
      }
    }
    
    
    //movies.modules.ts
    import { Module } from '@nestjs/common';
    import { MoviesController } from './movies.controller';
    import { MoviesService } from './movies.service';
    
    @Module({
      controllers: [MoviesController],
      providers: [MoviesService],
    })
    export class MoviesModule {}
    
    
    //movies.service.ts
    import { Injectable, NotFoundException } from '@nestjs/common';
    import { CreateMovieDto } from './dto/create-movie.dto';
    import { UpdateMovieDto } from './dto/update-movie.dto';
    import { Movie } from './entities/movie.entity';
    
    @Injectable()
    export class MoviesService {
      private movies: Movie[] = [];
    
      getAll(): Movie[] {
        return this.movies;
      }
    
      getOne(id: number): Movie {
        const movie = this.movies.find((movie) => movie.id === id);
        if (!movie) {
          throw new NotFoundException(`Movie with ID ${id} not found.`);
        }
        return movie;
      }
    
      deleteOne(id: number) {
        this.getOne(id);
        this.movies = this.movies.filter((movie) => movie.id !== id);
      }
    
      create(movieData: CreateMovieDto) {
        this.movies.push({
          id: this.movies.length + 1,
          ...movieData,
        });
      }
    
      update(id: number, updateData: UpdateMovieDto) {
        const movie = this.getOne(id);
        this.deleteOne(id);
        this.movies.push({ ...movie, ...updateData });
      }
    }
    
    
    //movies.controller.ts
    import {
      Body,
      Controller,
      Delete,
      Get,
      Param,
      Patch,
      Post,
    } from '@nestjs/common';
    import { CreateMovieDto } from './dto/create-movie.dto';
    import { UpdateMovieDto } from './dto/update-movie.dto';
    import { Movie } from './entities/movie.entity';
    import { MoviesService } from './movies.service';
    
    @Controller('movies')
    export class MoviesController {
      constructor(private readonly moviesService: MoviesService) {}
    
      @Get()
      getAll(): Movie[] {
        return this.moviesService.getAll();
      }
    
      @Get('/:id')
      getOne(@Param('id') movieId: number): Movie {
        return this.moviesService.getOne(movieId);
      }
    
      @Post()
      create(@Body() movieData: CreateMovieDto) {
        return this.moviesService.create(movieData);
      }
    
      @Delete('/:id')
      delete(@Param('id') movieId: number) {
        return this.moviesService.deleteOne(movieId);
      }
    
      @Patch('/:id')
      update(@Param('id') movieId: number, @Body() updateData: UpdateMovieDto) {
        return this.moviesService.update(movieId, updateData);
      }
    }
    
    
    //movie.entity.ts
    export class Movie {
      id: number;
      title: string;
      year: number;
      genres: string[];
    }
    
    
    //create-movie.dto.ts
    import { IsNumber, IsString, IsOptional } from 'class-validator';
    
    export class CreateMovieDto {
      @IsString()
      readonly title: string;
      @IsNumber()
      readonly year: number;
      @IsOptional()
      @IsString({ each: true })
      readonly genres: string[];
    }
    
    
    //update-movie.dto.ts
    import { PartialType } from '@nestjs/mapped-types';
    import { CreateMovieDto } from './create-movie.dto';
    
    export class UpdateMovieDto extends PartialType(CreateMovieDto) {}
    
    
    //movies.service.spec.ts
    import { NotFoundException } from '@nestjs/common';
    import { Test, TestingModule } from '@nestjs/testing';
    import { MoviesService } from './movies.service';
    
    describe('MoviesService', () => {
      let service: MoviesService;
    
      beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          providers: [MoviesService],
        }).compile();
    
        service = module.get<MoviesService>(MoviesService);
      });
    
      it('should be defined', () => {
        expect(service).toBeDefined();
      });
    
      describe('getAll', () => {
        it('should return an array', () => {
          const result = service.getAll();
          expect(result).toBeInstanceOf(Array);
        });
      });
    
      describe('getOne', () => {
        it('should return a movie', () => {
          service.create({
            title: 'Test Movie',
            genres: ['test'],
            year: 2000,
          });
          const movie = service.getOne(1);
          expect(movie).toBeDefined();
          expect(movie.id).toEqual(1);
        });
        it('should throw 404 error', () => {
          try {
            service.getOne(999);
          } catch (e) {
            expect(e).toBeInstanceOf(NotFoundException);
            expect(e.message).toEqual(`Movie with ID 999 not found.`);
          }
        });
      });
    
      describe('deleteOne', () => {
        it('deletes a movie', () => {
          service.create({
            title: 'Test Movie',
            genres: ['Test'],
            year: 2000,
          });
          const allMovies = service.getAll();
          service.deleteOne(1);
          const afterDelete = service.getAll();
          expect(afterDelete.length).toEqual(allMovies.length - 1);
        });
        it('should return a 404', () => {
          try {
            service.deleteOne(999);
          } catch (e) {
            expect(e).toBeInstanceOf(NotFoundException);
            expect(e.message).toEqual(`Movie with ID 999 not found.`);
          }
        });
      });
    
      describe('create', () => {
        it('should create a movie', () => {
          const beforeCreate = service.getAll().length;
          service.create({
            title: 'Test Movie',
            genres: ['Test'],
            year: 2000,
          });
          const afterCreate = service.getAll().length;
          expect(afterCreate).toEqual(beforeCreate + 1);
        });
      });
    
      describe('dupdate', () => {
        it('should update a movie', () => {
          service.create({
            title: 'Test Movie',
            genres: ['Test'],
            year: 2000,
          });
          service.update(1, { title: 'Updated Test' });
          const movie = service.getOne(1);
          expect(movie.title).toEqual('Updated Test');
        });
        it('should throw a NotFoundExe', () => {
          try {
            service.update(999, {});
          } catch (e) {
            expect(e).toBeInstanceOf(NotFoundException);
            expect(e.message).toEqual(`Movie with ID 999 not found.`);
          }
        });
      });
    });
    
    
    //app.e2e-spec.ts
    import { Test, TestingModule } from '@nestjs/testing';
    import { INestApplication, ValidationPipe } from '@nestjs/common';
    import * as request from 'supertest';
    import { AppModule } from './../src/app.module';
    
    describe('AppController (e2e)', () => {
      let app: INestApplication;
    
      beforeAll(async () => {
        const moduleFixture: TestingModule = await Test.createTestingModule({
          imports: [AppModule],
        }).compile();
    
        app = moduleFixture.createNestApplication();
        app.useGlobalPipes(
          new ValidationPipe({
            whitelist: true,
            forbidNonWhitelisted: true,
            transform: true,
          }),
        );
        await app.init();
      });
    
      it('/ (GET)', () => {
        return request(app.getHttpServer())
          .get('/')
          .expect(200)
          .expect('Welcome to my Movie API');
      });
    
      describe('/movies', () => {
        it('GET', () => {
          return request(app.getHttpServer()).get('/movies').expect(200).expect([]);
        });
        it('POST 201', () => {
          return request(app.getHttpServer())
            .post('/movies')
            .send({
              title: 'Test',
              year: 2000,
              genres: ['test'],
            })
            .expect(201);
        });
        it('POST 400', () => {
          return request(app.getHttpServer())
            .post('/movies')
            .send({
              title: 'Test',
              year: 2000,
              genres: ['test'],
              other: 'thing',
            })
            .expect(400);
        });
        it('DELETE', () => {
          return request(app.getHttpServer()).delete('/movies').expect(404);
        });
      });
    
      describe('/movies/:id', () => {
        it('GET 200', () => {
          return request(app.getHttpServer()).get('/movies/1').expect(200);
        });
        it('GET 404', () => {
          return request(app.getHttpServer()).get('/movies/999').expect(404);
        });
        it('PATCH 200', () => {
          return request(app.getHttpServer())
            .patch('/movies/1')
            .send({ title: 'Updated Test' })
            .expect(200);
        });
        it('DELETE 200', () => {
          return request(app.getHttpServer()).delete('/movies/1').expect(200);
        });
      });
    });