Database
Nest는 모든 데이터베이스로 모험을 시작하는 데 필요한 상용구를 줄이기 위해 @nestjs/typeorm
패키지를 사용할 준비가 되어 있습니다. TypeORM 은 지금까지 사용 가능한 가장 성숙한 ORM (Object Relational Mapper)이므로 선택했습니다. TypeScript로 작성되었으므로 Nest 프레임 워크에서 잘 작동합니다.
먼저 필요한 모든 종속성을 설치해야합니다.
Copy $ npm install --save @nestjs/typeorm typeorm mysql
info 알림 이 장에서는 MySQL 데이터베이스를 사용하지만 TypeORM 은 PostgreSQL, SQLite 및 MongoDB (NoSQL)와 같은 다양한 데이터베이스를 지원합니다.
설치 과정이 완료되면 TypeOrmModule
을 루트 ApplicationModule
로 가져올 수 있습니다.
Copy @@ filename ( app .module)
import { Module } from '@nestjs/common' ;
import { TypeOrmModule } from '@nestjs/typeorm' ;
@ Module ({
imports : [
TypeOrmModule .forRoot ({
type : 'mysql' ,
host : 'localhost' ,
port : 3306 ,
username : 'root' ,
password : 'root' ,
database : 'test' ,
entities : [__dirname + '/**/*.entity{.ts,.js}' ] ,
synchronize : true ,
}) ,
] ,
})
export class ApplicationModule {}
forRoot()
메소드는 TypeORM 패키지에서 createConnection()
과 동일한 구성 객체를 받습니다. 또한forRoot()
에 무엇이든 전달하는 대신 프로젝트 루트 디렉토리에 ormconfig.json
파일을 만들 수 있습니다.
Copy {
"type" : "mysql" ,
"host" : "localhost" ,
"port" : 3306 ,
"username" : "root" ,
"password" : "root" ,
"database" : "test" ,
"entities" : [ "src/**/*.entity{.ts,.js}" ] ,
"synchronize" : true
}
그런 다음 간단히 괄호를 비워 둘 수 있습니다.
Copy @@ filename ( app .module)
import { Module } from '@nestjs/common' ;
import { TypeOrmModule } from '@nestjs/typeorm' ;
@ Module ({
imports : [ TypeOrmModule .forRoot ()] ,
})
export class ApplicationModule {}
그런 다음 Connection
및 EntityManager
를 사용하여 다음과 같이 전체 프로젝트에 다른 모듈을 가져 오지 않고 삽입할 수 있습니다.
Copy @@ filename ( app .module)
import { Connection } from 'typeorm' ;
@ Module ({
imports : [ TypeOrmModule .forRoot () , PhotoModule] ,
})
export class ApplicationModule {
constructor ( private readonly connection : Connection ) {}
}
@@switch
import { Connection } from 'typeorm' ;
@ Dependencies (Connection)
@ Module ({
imports : [ TypeOrmModule .forRoot () , PhotoModule] ,
})
export class ApplicationModule {
constructor (connection) {
this .connection = connection;
}
}
Repository pattern
TypeORM 은 리포지토리 디자인 패턴을 지원하므로 각 엔터티에는 자체 리포지토리가 있습니다. 이 리포지토리는 데이터베이스 연결에서 얻을 수 있습니다.
첫째, 우리는 최소한 하나의 엔티티가 필요합니다. 공식 문서에서 Photo
엔티티를 재사용 할 것입니다.
Copy @@ filename ( photo .entity)
import { Entity , Column , PrimaryGeneratedColumn } from 'typeorm' ;
@ Entity ()
export class Photo {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ({ length : 500 })
name : string ;
@ Column ( 'text' )
description : string ;
@ Column ()
filename : string ;
@ Column ( 'int' )
views : number ;
@ Column ()
isPublished : boolean ;
}
Photo
엔티티는 photo
디렉토리에 속합니다. 이 디렉토리는 PhotoModule
을 나타냅니다. 모델 파일을 어디에 보관할지 결정해야 합니다. 우리의 관점에서, 해당 모듈 디렉토리에서 도메인 근처에 유지하는 가장 좋은 방법입니다.
PhotoModule
을 보겠습니다:
Copy @@ filename ( photo .module)
import { Module } from '@nestjs/common' ;
import { TypeOrmModule } from '@nestjs/typeorm' ;
import { PhotoService } from './photo.service' ;
import { PhotoController } from './photo.controller' ;
import { Photo } from './photo.entity' ;
@ Module ({
imports : [ TypeOrmModule .forFeature ([Photo])] ,
providers : [PhotoService] ,
controllers : [PhotoController] ,
})
export class PhotoModule {}
이 모듈은 forFeature()
메소드를 사용하여 현재 범위에 등록할 리포지토리를 정의합니다. 덕분에@InjectRepository()
데코레이터를 사용하여 PhotoRepository
를 PhotoService
에 주입할 수 있습니다.
Copy @@ filename ( photo .service)
import { Injectable } from '@nestjs/common' ;
import { InjectRepository } from '@nestjs/typeorm' ;
import { Repository } from 'typeorm' ;
import { Photo } from './photo.entity' ;
@ Injectable ()
export class PhotoService {
constructor (
@ InjectRepository (Photo)
private readonly photoRepository : Repository < Photo > ,
) {}
findAll () : Promise < Photo []> {
return this . photoRepository .find ();
}
}
@@switch
import { Injectable , Dependencies } from '@nestjs/common' ;
import { InjectRepository } from '@nestjs/typeorm' ;
import { Photo } from './photo.entity' ;
@ Injectable ()
@ Dependencies ( InjectRepository (Photo))
export class PhotoService {
constructor (photoRepository) {
this .photoRepository = photoRepository;
}
findAll () {
return this . photoRepository .find ();
}
}
warning 알림 PhotoModule
을 루트 ApplicationModule
로 가져 오는 것을 잊지 마십시오.
Multiple databases
일부 프로젝트에는 여러 개의 데이터베이스 연결이 필요할 수 있습니다. 다행히도 이 모듈을 사용하여 이 작업을 수행할 수도 있습니다. 여러 연결로 작업하려면 가장 먼저해야 할 일은 해당 연결을 만드는 것입니다. 이 경우 연결 이름이 필수 가 됩니다.
Person
엔티티와 Album
엔티티가 있고 각각 자체 데이터베이스에 저장되어 있다고 가정하십시오.
Copy const defaultOptions = {
type : 'postgres' ,
port : 5432 ,
username : 'user' ,
password : 'password' ,
database : 'db' ,
synchronize : true ,
};
@ Module ({
imports : [
TypeOrmModule .forRoot ({
... defaultOptions ,
host : 'photo_db_host' ,
entities : [Photo] ,
}) ,
TypeOrmModule .forRoot ({
... defaultOptions ,
name : 'personsConnection' ,
host : 'person_db_host' ,
entities : [Person] ,
}) ,
TypeOrmModule .forRoot ({
... defaultOptions ,
name : 'albumsConnection' ,
host : 'album_db_host' ,
entities : [Album] ,
}) ,
] ,
})
export class ApplicationModule {}
warning 알림 연결에 name
을 설정하지 않으면 이름이 default
로 설정됩니다. 이름이 없거나 이름이 같은 여러 개의 연결이 없어야 합니다. 그렇지 않으면 단순히 무시됩니다.
이 시점에서 각각의 Photo
, Person
및 Album
엔티티가 자체 연결에 등록되어 있습니다. 이 설정에서는TypeOrmModule.forFeature()
함수와 @InjectRepository()
데코레이터에게 어떤 연결을 사용해야 하는지 알려 주어야 합니다. 연결 이름을 전달하지 않으면 default
연결이 사용됩니다.
Copy @ Module ({
imports : [
TypeOrmModule .forFeature ([Photo]) ,
TypeOrmModule .forFeature ([Person] , 'personsConnection' ) ,
TypeOrmModule .forFeature ([Album] , 'albumsConnection' ) ,
] ,
})
export class ApplicationModule {}
주어진 연결에 대해 Connection
또는 EntityManager
를 삽입 할 수도 있습니다.
Copy @ Injectable ()
export class PersonService {
constructor (
@ InjectConnection ( 'personsConnection' )
private readonly connection : Connection ,
@ InjectEntityManager ( 'personsConnection' )
private readonly entityManager : EntityManager ,
) {}
}
Testing
응용 프로그램을 단위 테스트하는 경우 일반적으로 데이터베이스 연결을 피하여 테스트 슈트를 독립적으로 만들고 실행 프로세스를 가능한 한 빨리 만듭니다. 그러나 클래스는 연결 인스턴스에서 가져온 저장소에 따라 달라질 수 있습니다. 그럼 뭐야? 해결책은 가짜 저장소를 만드는 것입니다. 이를 달성하기 위해 커스텀 공급자 를 설정해야 합니다. 실제로 등록된 각 저장소는EntityNameRepository
토큰으로 표시됩니다. 여기서 EntityName
은 엔터티 클래스의 이름입니다.
@nestjs/typeorm
패키지는 주어진 엔티티를 기반으로 준비된 토큰을 반환하는 getRepositoryToken()
함수를 제공합니다.
Copy @ Module ({
providers : [
PhotoService ,
{
provide : getRepositoryToken (Photo) ,
useValue : mockRepository ,
} ,
] ,
})
export class PhotoModule {}
이제 하드 코딩 된 mockRepository
가 PhotoRepository
로 사용됩니다. 공급자가 @InjectRepository()
데코레이터를 사용하여 PhotoRepository
를 요청할 때마다 Nest는 등록 된 mockRepository
객체를 사용합니다.
Custom repository
TypeORM은 사용자 정의 저장소 라는 기능을 제공합니다. 이에 대한 자세한 내용은 여기 페이지를 방문하십시오. 기본적으로 사용자 정의 저장소를 사용하면 기본 저장소 클래스를 확장하고 몇 가지 특수한 방법으로 이를 강화할 수 있습니다.
커스텀 저장소를 만들려면 @EntityRepository()
데코레이터를 사용하고 Repository
클래스를 확장하십시오.
Copy @ EntityRepository (Author)
export class AuthorRepository extends Repository < Author > {}
info 힌트 @EntityRepository()
와 Repository
는 모두typeorm
패키지에서 나옵니다.
클래스가 생성되면 다음 단계는 인스턴스화 책임을 Nest에 넘겨주는 것입니다. 이를 위해 AuthorRepository
클래스를 TypeOrm.forFeature()
메소드에 전달해야 합니다.
Copy @ Module ({
imports : [ TypeOrmModule .forFeature ([AuthorRepository])] ,
controller : [AuthorController] ,
providers : [AuthorService] ,
})
export class AuthorModule {}
그런 다음 다음 구성을 사용하여 저장소를 주입하십시오.
Copy @ Injectable ()
export class AuthorService {
constructor ( private readonly authorRepository : AuthorRepository ) {}
}
Async configuration
모듈 옵션을 미리 전달하는 대신 비동기식으로 전달하려는 경우가 종종 있습니다. 이 경우 비동기 데이터를 처리하는 몇 가지 다양한 방법을 제공하는 forRootAsync()
메서드를 사용하십시오.
가능한 첫 번째 방법은 팩토리 기능을 사용하는 것입니다.
Copy TypeOrmModule .forRootAsync ({
useFactory : () => ({
type : 'mysql' ,
host : 'localhost' ,
port : 3306 ,
username : 'root' ,
password : 'root' ,
database : 'test' ,
entities : [__dirname + '/**/*.entity{.ts,.js}' ] ,
synchronize : true ,
}) ,
});
분명히, 우리 팩토리는 다른 모든 것처럼 행동합니다 ( 비동기(aync)
일 수도 있고 주입(inject)
을 통해 의존성을 주입할 수도 있습니다).
Copy TypeOrmModule .forRootAsync ({
imports : [ConfigModule] ,
useFactory : async (configService : ConfigService ) => ({
type : 'mysql' ,
host : configService .getString ( 'HOST' ) ,
port : configService .getString ( 'PORT' ) ,
username : configService .getString ( 'USERNAME' ) ,
password : configService .getString ( 'PASSWORD' ) ,
database : configService .getString ( 'DATABASE' ) ,
entities : [__dirname + '/**/*.entity{.ts,.js}' ] ,
synchronize : true ,
}) ,
inject : [ConfigService] ,
});
또는 팩토리 대신 클래스를 사용할 수 있습니다.
Copy TypeOrmModule .forRootAsync ({
useClass : TypeOrmConfigService ,
});
위의 구성은 TypeOrmModule
내에서 TypeOrmConfigService
를 인스턴스화 하고이를 활용하여 옵션 개체를 만듭니다. TypeOrmConfigService
는 TypeOrmOptionsFactory
인터페이스를 구현해야 합니다.
Copy @ Injectable ()
class TypeOrmConfigService implements TypeOrmOptionsFactory {
createTypeOrmOptions () : TypeOrmModuleOptions {
return {
type : 'mysql' ,
host : 'localhost' ,
port : 3306 ,
username : 'root' ,
password : 'root' ,
database : 'test' ,
entities : [__dirname + '/**/*.entity{.ts,.js}' ] ,
synchronize : true ,
};
}
}
TypeOrmModule
내에 TypeOrmConfigService
가 작성되는 것을 방지하고 다른 모듈에서 가져온 제공자를 사용하려면 useExisting
구문을 사용할 수 있습니다.
Copy TypeOrmModule .forRootAsync ({
imports : [ConfigModule] ,
useExisting : ConfigService ,
});
하나의 중요한 차이점으로 useClass
와 동일하게 작동합니다. TypeOrmModule
은 가져온 모듈을 조회하여 이미 생성된 ConfigService
를 자체적으로 인스턴스화하는 대신 재사용합니다.
Example
실제 예제는 여기 에서 볼 수 있습니다.