TypeORM 과 API
그동안 express로 서버개발을 꽤많이 해왔고, 다른 커뮤니티를 찾아보지 않아도 느꼈던, express의 자유도는 오히려 장기적으로, 엔터프라이즈 용으로는 분리한 점이 느껴졌다.
중간중간 Nest의 핵심 개념과 기능들을 정리해 보고자 한다.
공식문서
TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server,
typeorm.io
공식문서는 아니지만, 검색시 노출이 많이되는 꽤 괜찬은 문서
README_ko - typeorm
이 예시에서는 MySQL을 사용하고 있지만 지원되는 다른 데이터베이스를 사용할 수도 있다. 다른 데이터 베이스를 사용하려면 옵션의 type을 사용 중인 데이터베이스 타입으로 변경하기만 하면 된
orkhan.gitbook.io
DB연결하기
서버에서 가장중요한것이 DB와 연결된 API를 제공하는 것이라 생각한다.
인증, dto 이런것들 이 이후라고 생각한다.
mysql + typeorm 설치
npm install --save @nestjs/typeorm typeorm mysql2
docker db 설치
설치후 스키마도 하나 생성해준다.
docker run --name my-mysql -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:latest
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '1234',
database: 'kong',
synchronize: true,
keepConnectionAlive: true,
logging: 'all',
charset: 'utf8mb4',
autoLoadEntities: true,
}),
PostsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- type: db 종류
- host: db 주소
- port: db포트(mysql은 보통 3306)
- username: db 사용자이름
- password: db 사용자 비번
- database: db 데이터베이스 이름
- entities: 만든 엔티티들 여기에 넣기
- autoLoadEntities: true면 TypeOrmModule.forFeature에 넣은 엔티티들은 자동 연결됨
- synchronize: true면 entity에 따라 실제 db 테이블 재생성(기존 테이블 다 날라가니 주의)
- logging: true면 typeorm이 만들어주는 쿼리가 콘솔에 표시(개발 시에는 켜두는 게 매우 유용)
- keepConnectionAlive: true여야 핫 리로딩시에도 db 연결이 끊어지지 않음
- charset: db의 언어 설정(utf8mb4로 할것)
엔티티 생성(테이블 생성)
Repository pattern
TypeORM을 사용하는 패턴은 여러개가 있다. 그중에서 개인적으로 repository 패턴을 선호한다.(이유는 처음이걸로 접했으니까..)
각 Entity는 각각의 Repository(저장공간)이 있다.
Entity(테이블)정의
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class Article {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ type: 'text' })
content: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
모듈, 컨트롤러, 서비스 생성하기
nest에서는 큰 기능 범주로 모듈을 나누고, 모듈 내에서 요청과 응답을 처리하는 controller, 비지니스로직이 들어가는 service 3가지로 구성된다.
nest g mo posts // 모듈 생성
nest g co posts // 컨트롤러 생성
nest g s posts // 서비스 생성
API 생성하기
provider는 단순히 @Injectable() 데코레이터 주석이 달린 클래스를 말한다. provider 객체는 런타임에 NestJS에 의해 다른 모듈로 주입될 수 있다.
provider는 service를 포함해 repository, factory, helper 등이 될 수 있다.
@Injectable() 데코레이터가 붙어있는 클래스는 constructor를 통해서 의존성을 주입할 수 있다. 이렇게 하면 클래스 내부에서 this를 통해서 해당 서비스에 접근할 수 있게 된다.
import { Controller, Get, Post } from '@nestjs/common';
import { PostsService } from './posts.service';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll() {
return this.postsService.findAll();
}
@Post()
create() {
return this.postsService.createOne();
}
}
import { Injectable } from '@nestjs/common';
@Injectable()
export class PostsService {
findAll() {
return 'findAll';
}
createOne() {
return 'create';
}
}
TypeORM Dependency Injection
Post데코레이터와 겹쳐서 Entity의 이름을 Article로 변경하였습니다
@Module({
imports: [TypeOrmModule.forFeature([Article])],
controllers: [PostsController],
providers: [PostsService],
})
import { Body, Controller, Get, Post } from '@nestjs/common';
import { PostsService } from './posts.service';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll() {
return this.postsService.findAll();
}
@Post()
create(@Body() body: any) {
return this.postsService.createOne(body);
}
}
조회, 생성
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Article } from '../entity/article.entity';
import { Repository } from 'typeorm';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Article)
private articleRepository: Repository<Article>,
) {}
findAll() {
return this.articleRepository.find();
}
async createOne(body) {
const { title, content } = body;
const newArticle = this.articleRepository.create({ title, content });
return this.articleRepository.save(newArticle);
}
}
수정,삭제
@Patch(':id')
update(@Param('id') id: string, @Body() body: any) {
return this.postsService.updateById(+id, body);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.postsService.removeById(+id);
}
updateById(id: number, body: any) {
return this.articleRepository.update(id, body);
}
removeById(id: number) {
return this.articleRepository.delete(id);
}