Nest는 MongoDB 데이터베이스와의 통합을 위해 두가지 방법을 지원한다.
MongoDB용 커넥터가 있는 내장 TypeORM
모듈을 사용하거나 MongoDB 개체 모델링 도구인 Mongoose
를 사용할 수 있다. TypeORM은 아직까지 몽고디비와 정확히 호환되지 않아 복잡한 작업을 처리할 때 어려움이 있다. 따라서 우리는 전용 @nestjs/mongoose
패키지를 사용하여 데이터베이스를 연결했다.
$ npm install --save @nestjs/mongoose mongoose
설치가 완료되면 MongooseModule을 루트 모듈로 가져올 수 있다.
// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [Mongoose.forRoot(process.env.MONGO_URL)],
})
export class AppModule {}
forRoot()
메소드는 Mongoose 패키지의 mongoose.connect()
와 동일한 구성 객체를 허용한다.
모듈 옵션을 비동기적으로 전달해야 하는 경우 forRootAsync()
메서드를 사용한다. 팩토리 기능을 사용하면 비동기 구성을 처리할 수 있다. 다른 팩토리 프로바이더와 마찬가지로 팩토리 함수는 async 일 수 있으며, inject를 통해 종속성을 삽입한다.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URL'),
}),
inject: [ConfigService],
});
],
...
})
export class AppModule {}
Mongoose는 모든 것이 스키마에서 파생된다. 스키마는 모델을 정의하는데 사용된다. NestJS 데코레이터 @Schema()
를 사용하여 클래스를 스키마 정의로 표시한다.
// schemas/order.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type OrderDocument = Order & Document;
@Schema()
export class Order {
@Prop()
barcode: string;
@Prop()
userName: string;
@Prop()
price: number;
}
export const OrderSchema = SchemaFactory.createForClass(Order);
Order 클래스를 같은 이름의 MongoDB 컬렉션에 매핑하지만 끝에 's'를 추가하여 최종 몽고 컬렉션 이름은 orders가 된다.
@Prop()
데코레이터는 문서의 속성을 정의한다. 위의 스키마 정의에서 barcode, userName, price 세가지 속성을 정의했다. 이러한 속성의 스키마 유형은 타입스크립트 메타데이터 기능 덕분에 자동 유추된다. 하지만 형식을 반영할 수 없는 경우, 아래와 같이 명시적으로 표시한다.
@Prop(String)
barcode: string;
아래는 필수 여부, any 타입 선언, 다른 모델과의 관계 정의를 지정한 경우의 예시이다.
import * as mongoose from 'mongoose';
import { Price } from './schemas/price.schema';
@Prop({ required: true })
barcode: string;
@Prop({ type: mongoose.Schema.Types.Mixed })
userName: any;
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Price' })
price: Price;
스키마 파일은 사용할 모듈에 저장하는 곳이 좋다. MongooseModule
은 모듈을 구성하기 위한 forFeature()
메소드를 제공하며 현재 범위에 등록해야하는 모듈을 정의한다.
// order.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { OrderController } from './order.controller';
import { OrderService } from './order.service';
import { Order, OrderSchema } from './schemas/order.schema';
@Module({
imports: [MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }])],
controllers: [OrderController],
providers: [OrderService],
})
export class OrderModule {}
스키마를 등록한 후 @InjectModel()
데코레이터를 사용하여 Order 모델을 OrderService에 삽입할 수 있다.
// order.service.ts
import { Model } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Order, OrderDocument } from './schemas/order.schema';
@Injectable()
export class OrderService {
constructor(@InjectModel(Order.name) private orderModel: Model<OrderDocument>) {}
async getOrder(barcode: string): Promise<any> {
try {
const result = await this.orderModel.findOne({ barcode }).lean();
return result;
} catch (err) {
console.log('error...');
}
}
}
여러 데이터베이스 연결이 필요한 경우, 모듈을 통해서 가능하다. 이 경우 연결 이름 지정은 필수이다. 이름이 없거나 같은 이름의 연결이 여러개 있으면 안되며, connectionName
을 사용한다.
// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/database1', {
connectionName: 'database1',
}),
MongooseModule.forRoot('mongodb://localhost/database2', {
connectionName: 'database2',
}),
],
})
export class AppModule {}
이 설정을 사용하려면 forFeature()
함수에 어떤 연결을 사용해야 하는지 명시해야 한다.
@Module({
imports: [
MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }], 'database1'),
],
})
팩토리 프로바이더를 사용하는 경우, 연결 이름을 인수로 전달하는 getConnectionToken()
함수를 사용한다. (질문 ✅)
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URL'),
}),
inject: [ConfigService, getConnectionToken('database1')],
});