Skip to main content

Multiple Connection

之前 已經設定了 MongoDB 的連線, 這邊紀錄如何同時連線多個 MongoDB

將第二組連線用的機密資訊設定在 config/env 中, 並且加上 CONNECTION_NAME 來區分

env 記得不要放到 cloud

.env
APP_DOMAIN=YOUR_DOMAIN
MONGO_DB_NAME=YOUR_DB_NAME
MONGO_DB_MAIN_CONNECTION_NAME=YOUR_MAIN_CONNECTION_NAME
MONGO_DB_SUB_CONNECTION_NAME=YOUR_SUB_CONNECTION_NAME
MONGO_SESSION_DB_COLLECTION=YOUR_COLLECTION_NAME
MONGO_SESSION_DOMAIN=.${APP_DOMAIN}
MONGO_SESSION_NAME=
MONGO_SESSION_SECRET=
MONGO_URI=mongodb://127.0.0.1:27017

調整 configuration.factory.tsconfiguration.import.ts 中的設定:

configuration.factory.ts
import { registerAs } from '@nestjs/config';
export const databaseConfigFactory = registerAs('database', () => {
const mainUri = `${process.env.MONGO_URI}/${process.env.MONGO_DB_MAIN_CONNECTION_NAME}`;
const subUri = `${process.env.MONGO_URI}/${process.env.MONGO_DB_SUB_CONNECTION_NAME}`;

return {
mainUri,
subUri,
ssl: process.env.SSL
};
});
constants.ts
import * as process from 'node:process';
import * as dotenv from 'dotenv';

export const dbConnectionName: {
MAIN: string;
SUB: string;
} = {
MAIN: process.env.MONGO_DB_CONNECTION_NAME,
SUB: process.env.MONGO_SUB_CONNECTION_NAME
};
configuration.import.ts
// ...
import * as dotenv from 'dotenv';

export function loadConfigImports() {
const main = process.env.MONGO_DB_CONNECTION_NAME;
const sub = process.env.MONGO_SUB_CONNECTION_NAME;

return [
//...
MongooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
ssl: configService.get<boolean>('database.ssl'),
uri: configService.get<string>('database.mainUri')
}),
connectionName: main
}),
MongooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
ssl: configService.get<boolean>('database.ssl'),
uri: configService.get<string>('database.subUri')
}),
connectionName: sub
})
];
}
note

在 build 的時候, env 需要依靠 dotenv 來讀取, 避免啟動時一開始是會沒有讀到 env 資料的狀況, 造成 db 並沒有正確對應到該 connection name

建立兩個連結不同 db 的 feature 來測試

建立第一組 module/controller/service

user.service.ts
@Injectable()
export class UserService {
constructor(
@InjectModel(USER_MODEL_TOKEN, dbConnectionName.MAIN)
private readonly userModel: Model<UserDocument>
) {}
async findUserByEmail({ email }: { email: string }) {
const filter: FilterQuery<UserDocument> = { email };
return this.userModel.findOne(filter).lean();
}
}
  • Service 中使用時, 需要在 @InjectModel() 指定使用哪個 MongoDB 連線, 這是因為 Service 需要知道要注入的 Module 是屬於哪一個 DB 連線來正確地操作相應的資料, 確保在該 Service 中所操作的資料是來自指定的 database
user.controller.ts
@Controller('main/user')
export class UserController {
constructor(private readonly userService: UserService) {}

@Get()
async getUserByEmail(@Query('email') email: string) {
return this.userService.findUserByEmail({ email });
}
}
user.module.ts
// ...
@Module({
imports: [MongooseModule.forFeature([UserDefinition], dbConnectionName.MAIN)],
providers: [UserService],
exports: [UserService],
controllers: [UserController]
})
export class UserModule {}
  • Module 中 import MongooseModule.forFeature 時, 同樣需要指定連線名稱, 這是因為 MongooseModule.forFeature 負責為該 Module 提供所需的Module 定義和資料庫連線, 確保在這個 Module 中所使用的所有 Module 和資料庫操作都會指向正確的 database connection

建立第二組 module/controller/service

todo.service.ts
@Injectable()
export class TodoService {
constructor(
@InjectModel(TODO_MODEL_TOKEN, dbConnectionName.SUB)
private readonly todoModel: Model<TodoDocument>
) {}
async findTodoById({ _id }: { _id: string }) {
const filter: FilterQuery<TodoDocument> = { _id };
return this.todoModel.findOne(filter).lean();
}
}
todo.controller.ts
@Controller('sub/todo')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Get(':_id')
async getTodoById(@Param() _id: string) {
return this.todoService.findTodoById({ _id });
}
}
todo.module.ts
// ...
@Module({
imports: [MongooseModule.forFeature([TodoDefinition], dbConnectionName.SUB)],
providers: [TodoService],
controllers: [TodoController],
exports: [TodoService]
})
export class TodoModule {}

app.module.ts 中 import loadConfigImports 及兩個 module

app.module.ts
// ...
@Module({
imports: [...loadConfigImports(), UserModule, TodoModule]
// ...
})
export class AppModule {}

這樣就可以同時連線多個 MongoDB 了