# Node中如何使用Nest.js連接MongoDB數據庫
## 目錄
1. [Nest.js與MongoDB概述](#nestjs與mongodb概述)
2. [環境準備與項目初始化](#環境準備與項目初始化)
3. [MongoDB模塊配置](#mongodb模塊配置)
4. [定義數據模型與Schema](#定義數據模型與schema)
5. [Repository模式實現](#repository模式實現)
6. [服務層與業務邏輯](#服務層與業務邏輯)
7. [控制器與路由處理](#控制器與路由處理)
8. [高級查詢與聚合操作](#高級查詢與聚合操作)
9. [性能優化與最佳實踐](#性能優化與最佳實踐)
10. [常見問題解決方案](#常見問題解決方案)
---
## Nest.js與MongoDB概述
### 為什么選擇Nest.js + MongoDB組合
Nest.js是一個基于TypeScript的漸進式Node.js框架,結合了OOP(面向對象編程)、FP(函數式編程)和FRP(函數響應式編程)的理念。MongoDB作為領先的NoSQL數據庫,具有以下優勢:
- 文檔型數據模型(BSON格式)
- 靈活的模式設計
- 水平擴展能力
- 豐富的查詢語言
兩者結合特別適合:
- 快速迭代的現代Web應用
- 需要處理非結構化數據的場景
- 微服務架構中的數據存儲
### 技術棧核心組件
| 組件 | 作用 |
|-------------------|-----------------------------|
| @nestjs/mongoose | 官方MongoDB集成包 |
| mongoose | MongoDB對象建模工具 |
| Typegoose | 用TypeScript類定義模型的替代方案 |
---
## 環境準備與項目初始化
### 系統要求
- Node.js 16+
- MongoDB 4.4+
- npm 8+ 或 yarn 1.22+
### 創建Nest項目
```bash
npm i -g @nestjs/cli
nest new nest-mongo-demo
cd nest-mongo-demo
npm install @nestjs/mongoose mongoose
npm install --save-dev @types/mongoose
/src
├── /modules
│ ├── /database
│ ├── /users
│ └── /products
├── main.ts
└── app.module.ts
// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/nest-demo', {
connectionFactory: (connection) => {
connection.on('connected', () => {
console.log('MongoDB connected successfully');
});
return connection;
}
})
]
})
export class AppModule {}
// config/configuration.ts
export default () => ({
database: {
uri: process.env.MONGO_URI || 'mongodb://localhost:27017/nest-demo',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
authSource: 'admin',
user: process.env.MONGO_USER,
pass: process.env.MONGO_PASS
}
}
});
// app.module.ts
MongooseModule.forRootAsync({
useFactory: (config: ConfigService) => ({
uri: config.get<string>('database.uri'),
...config.get('database.options')
}),
inject: [ConfigService]
})
// users/schemas/user.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
@Schema({ timestamps: true })
export class User extends Document {
@Prop({ required: true })
username: string;
@Prop({ required: true, select: false })
password: string;
@Prop({ required: true, unique: true })
email: string;
@Prop({ default: false })
isVerified: boolean;
}
export const UserSchema = SchemaFactory.createForClass(User);
@Schema()
export class Product {
@Prop({ index: true })
name: string;
@Prop({ index: 'text' })
description: string;
}
// 復合索引
ProductSchema.index({ name: 1, price: -1 });
UserSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
UserSchema.pre('save', function(next) {
if (this.isModified('password')) {
this.password = hashPassword(this.password);
}
next();
});
// users/repositories/user.repository.ts
import { Model } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { User } from '../schemas/user.schema';
@Injectable()
export class UserRepository {
constructor(@InjectModel(User.name) private userModel: Model<User>) {}
async create(createUserDto: any): Promise<User> {
const createdUser = new this.userModel(createUserDto);
return createdUser.save();
}
async findAll(): Promise<User[]> {
return this.userModel.find().exec();
}
async findById(id: string): Promise<User | null> {
return this.userModel.findById(id).exec();
}
}
async findWithPagination(
query: any,
page: number,
limit: number
): Promise<{ data: User[]; total: number }> {
const [data, total] = await Promise.all([
this.userModel
.find(query)
.skip((page - 1) * limit)
.limit(limit)
.exec(),
this.userModel.countDocuments(query).exec()
]);
return { data, total };
}
// users/services/user.service.ts
import { Injectable } from '@nestjs/common';
import { UserRepository } from '../repositories/user.repository';
@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
async createUser(createUserDto: any) {
// 業務邏輯驗證
if (await this.userRepository.exists({ email: createUserDto.email })) {
throw new Error('Email already exists');
}
return this.userRepository.create(createUserDto);
}
}
async transferFunds(senderId: string, receiverId: string, amount: number) {
const session = await this.userModel.db.startSession();
session.startTransaction();
try {
const sender = await this.userModel.findByIdAndUpdate(
senderId,
{ $inc: { balance: -amount } },
{ session }
).exec();
const receiver = await this.userModel.findByIdAndUpdate(
receiverId,
{ $inc: { balance: amount } },
{ session }
).exec();
await session.commitTransaction();
return { sender, receiver };
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
// users/controllers/user.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { UserService } from '../services/user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
async create(@Body() createUserDto: any) {
return this.userService.createUser(createUserDto);
}
@Get()
async findAll() {
return this.userService.findAll();
}
}
@Controller({ path: 'users', version: '1' })
@Post('avatar')
@UseInterceptors(FileInterceptor('file'))
async uploadAvatar(
@UploadedFile() file: Express.Multer.File,
@User() user: User
) {
return this.userService.updateAvatar(user._id, file);
}
// 多條件聯合查詢
async findActiveUsers(minAge: number) {
return this.userModel.find({
age: { $gte: minAge },
isActive: true,
lastLogin: { $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
}).sort({ createdAt: -1 });
}
async getUserStats() {
return this.userModel.aggregate([
{
$match: { isActive: true }
},
{
$group: {
_id: '$role',
count: { $sum: 1 },
averageAge: { $avg: '$age' }
}
},
{
$sort: { count: -1 }
}
]);
}
const explanation = await userModel.find(query).explain();
UserSchema.index({ email: 1 }, { unique: true });
MongooseModule.forRoot(uri, {
poolSize: 20, // 默認5
socketTimeoutMS: 45000,
connectTimeoutMS: 30000
})
const readPreference = process.env.NODE_ENV === 'production'
? 'secondaryPreferred'
: 'primary';
MongooseModule.forRoot(uri, { readPreference });
try {
await userRepository.create(userData);
} catch (error) {
if (error.code === 11000) {
throw new ConflictException('Duplicate key error');
}
throw error;
}
mongoose.connection.on('error', (err) => {
logger.error(`MongoDB connection error: ${err}`);
process.exit(1);
});
async function migrateUserData() {
const batchSize = 100;
let processed = 0;
const cursor = oldUserModel.find().cursor();
for await (const doc of cursor) {
await newUserModel.create(transformData(doc));
processed++;
if (processed % batchSize === 0) {
console.log(`Processed ${processed} documents`);
}
}
}
本文詳細介紹了在Nest.js中集成MongoDB的全流程,從基礎連接到高級優化,涵蓋了: - 模塊化數據庫配置 - 類型安全的Schema設計 - 分層的應用架構 - 事務處理和聚合查詢 - 生產環境最佳實踐
通過合理運用這些技術,您可以構建出高性能、易維護的Node.js后端服務。建議進一步探索: - MongoDB Atlas云服務集成 - 分布式事務處理 - 與GraphQL的深度整合 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。