https://github.com/leye195/nc-nest
Learning NestJS by making API
https://github.com/leye195/nc-nest
nestjs typescript
Last synced: 3 months ago
JSON representation
Learning NestJS by making API
- Host: GitHub
- URL: https://github.com/leye195/nc-nest
- Owner: leye195
- Created: 2020-11-22T13:17:20.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2020-12-06T09:03:29.000Z (over 4 years ago)
- Last Synced: 2025-02-06T23:31:27.852Z (4 months ago)
- Topics: nestjs, typescript
- Language: TypeScript
- Homepage:
- Size: 219 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Learn Nest
Learn how to build NodeJS applications using NestJS
NestJS는 프레임워크이며 미리 세팅돈 여러 기능들을 제공한다.
@nestjs/cli 설치 필요
```
npm i -g @nestjs/cli
nest new project-name
```NestJS는 `main.ts` 파일을 가짐
```
//main.ts, nestJS는 main.ts에서 시작하며 하나의 모듈에서 어플리케이션을 생성한다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();//app.module.ts, 모든 것의 루트 모듈 같은 것,
//모듈은 어플리케이션의 일부분 이라고 할 수 있다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({ //데코레이터 함수, nestjs는 데코레이터와 함께하기 때문에 익숙해질 필요가 있음
imports: [],
controllers: [AppController], //기본적으로 url을 가져오고 함수를 실행함
providers: [AppService], //
})
export class AppModule {}
// 데코레이터는 클래스에 함수 기능을 추가 할 수 있다.
// 그냥 클래스 위의 함수이며, 클래스를 위해 움직임//app.controllers.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}@Get() //express의 get 라우터와 같은 역활
getHello(): string {
return this.appService.getHello()
}// 여기서는 Service를 참조@Get('/hello') // /hello를 getSayHi 함수로 맵핑 시켜줌
getSayHi(): string {
return 'hi';
}
//데코레이터는 항상 꾸며주는 함수나 클래스랑 붙어있어야 된다
}
```## Service와 Controller
- NestJS는 Controller를 비즈니스 로직이랑 구분 짓고 싶어한다.
- Controller 는 그냥 url을 가져오는 함수 실행결과를 리턴 하는 역할을
- Service를 통해 비즈니스 로직을 구현한다.## Rest API 만들기
```
// controller 생성해줌
nest g co
or
nest generate co
``````
import { Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';@Controller('movies') // 입력된 값이 특별하게 취급되어, 컨트롤러를 위한 url을 만듬, 즉 Entry Point를 컨트롤 함
export class MoviesController {
@Get()
getAll() {
return 'This will return movies';
}@Get('/:id')
getMovie(@Param('id') id: string) {
return `Return one movie with the id: ${id}`;
}@Post()
createMovie() {
return 'This will create a movie';
}@Delete('/:id')
deleteMovie(@Param('id') movieId: string) {
return `Delete Movie with id: ${movieId}`;
}@Patch('/:id')
pathMovie(@Param('id') movieId: string) {
return `This will patch a movie ${movieId}`;
}
}
//nest의 경우 무건가 필요하면 요청을 해줘야된다,
//url에 있는 id값을 알고 싶은 경우, @Param('id') id: string 을 작성
//@Body
```**Movies Service**
Single-responsibility principle
하나의 module, class 혹은 function이 하나만의 기능을 책임 져야된다.
**Services: 로직을 관리하는 역할**
```
// service 생성
nest g service (s)or
nest generate service (s)
``````
/* service의 이름을 입력
ex) movies, 아래와 같은 두 파일이 생성 됨
movies.service.spec (테스트 파일)
movies.service.tsapp.module.ts의 providers에 자동으로 MovieService가 추가됨
*/import { Injectable } from '@nestjs/common';
import { Movie } from './entities/movie.entity';@Injectable()
export class MoviesService {
private movies: Movie[] = [];getAll(): Movie[] {
return this.movies;
}getOne(movieId: string): Movie {
const movie = this.movies.find((movie) => movie.id === +movieId);
if (!movie) {
new throw NotFoundException("");
}
return movie;
}create(movieData) {
this.movies.push({
id: this.movies.length + 1,
...movieData,
})
}delete(movieId: string): boolean {
this.movies.filter((movie)=>movie.id !== +movidId);
return true;
}update(movieId: string, updatedMovie) {
const movie = this.getOne(movieId);
this.delete(movieId);
this.movies.push({...movie,...updateMovie});
}
}
```**Controller에서 Service에 접근하는 방법**
- express와 달리 nest에서는 요청을 해야된다
```
@Controller('movies');
export class MoviesController {
constructor(private readonly moviesService: MoviesService){}@Get()
getAll: Movie[] {
return this.moviesService.getAll();
}@Get('/:id')
getOne(@Param('id') id: string): Movie {
return this.moviesService.getOne(id);
}@Post()
create(@Body() movieData) {
return this.moviesService.create(movieData);
}@Delete()
delete(@Param('id') id: string) :boolean {
return this.moviesService.delete(id);
}
}
```**DTO: Data Transfer Object (데이터 전송 객체)**
```
//create-movie.dto.tsimport {IsString, IsNumber} from 'class-validator';
//class-validator
//class-transformexport class CreateMovieDTO {
@IsString()
readonly title: string;@IsNumber()
readonly year: number;@IsString()
readonly genres: string[];
}export class UpdateMovieDTO extends PartialType(baseType){}
//PartialType() - @nextjs/mapped-types```
nestJS는 타입을 받아서 넘겨줄 때 자동으로 타입을 변환해 주도록 설정해 줄 수 있음.
```
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';async function bootstrap() {
const app = await NestFactory.create(AppModule);
// validation pipe (middle 비슷하게 생각해도 됨)
app.useGlobalPipes(
new ValidationPipe({
forbidNonWhitelisted: true,
whitelist: true,
transform: true, // 유저가 보낸 것을 원하는 실제 타입으로 전환
}),
);
await app.listen(3000);
}
bootstrap();
```**app.module.ts, modules and dependency injection**
- app.module은 원래 AppController와 AppProvider만 가지고 있어야 된다.
- NestJS에서 앱은 여러개의 모듈로 구성되기 때문에 분리된 모듈들을 app.module을 통해 import 해주자```
// module 생성
nest g(generate) mo (module)
``````
//app.module의 imports에 생성된 module이 추가됨
//생성한 movies.module.ts에 controllers와 providers를 추가해줌//app.module.ts
@Module({
imports: [MoviesModule]
controllers: [],
providers: [],
})
export class AppModule {}//movies.module.ts
@Module({
controllers: [MoviesController],
providers: [MovieService],
})
export class MovieModule {}
```**dependency injection**
- providers에 Service를 제공하면 nestJS는 providers에 제공된 Service를 import 하고 Controller에 inject해준다.
- Service에 @Injectable이라는 decorator가 있는데 nestJS가 알아서 import 해주는데 필요함**nestJS에서 req,res 접근**
- 기본적으로 nestJS는 Express 위에서 돌아가고 있기에 Controller에 Request, Response 객체가 필요하다면 사용할 수 있음
- req, res 같은 express 객체를 직접적으로 사용하는 방법은 좋은 방법이 아님
- 이유: 2개의 framework 위에서 동시에 작동하고 있기 때문 (Fastify, Express)```
// ex)
@Get()
getAll(@Req() req, @Res() res): Movie[] {}
```## Testing
- unit testing은 모든 function을 따로 따로 테스트하는 것을 의미
- end-to-end (e2e) 라는 테스트도 있는데, 이것은 모든 시스템을 테스팅 하는 것* 주의: e2e 테스트, unit 테스트를 진행시 실제 application의 환경을 그대로 적용시켜줘야 된다.
package.json 파일에 5가지 테스트 관련 스트립트가 있음
- test: "jest"
- test:watch : "jest --watch"
- test:cov : "jest --coverage"
- test:debug : "node --inspect-brk -r tsconfig-paths/register -r ts-node/reg
- test:e2e : "jest --config ./test/jest-e2e.json"### jest
- jest는 javascript를 아주 쉽게 테스팅하게 도와주는 패키지 (TS, Node, React 등등에서 모두 테스트 가능)
- nestJS에서 `.spec.ts` 가 붙은 파일들은 테스트를 포함하는 파일이다
- nestJS에서는 jest가 .spec.ts 파일들을 찾아 볼 수 있도록 설정 되어 있음
- jest 참고 문서 링크: https://jestjs.io/docs/en/api#reference**Unit Test**
```
describe(); //테스트 묘사beforeEach(); // 각 테스트가 진행되기 전에 실행
it(name,callback); // individual test (개별 테스트)
expect(expression); //결과 예측하는데 활용
ex) expect(received).toEqual(expected)
expect(2 + 2).toEqual(4);afterAll(); // // 모든 테스트가 완료 된 후 실행됨, 안에 DB를 깨끗하게 정리해주는 function을 넣을 수 있음
afterEach(); // 각 하나의 테스트가 완료 된 후 실행됨
beforeAll(); // DB를 정리하고 테스트 데이터를 추가해줄수 있음...
```
**e2e Test**
- test 폴더의 app.e2e-spec.ts에서 테스트 진행
- unit test와 다른 점은 supertest 라는 lib을 활용해 http request를 전달 할 수 있음```
//ex)
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Welcome to Movie API');
});// it.todo() 활용 작성하고 싶은 내용 입력해 줄 수 있음
it.todo("GET");
it.todo("PATCH");
it.todo("DELETE");
```