NestJS MongoDB集成 #
MongoDB简介 #
MongoDB是一个基于文档的NoSQL数据库,使用BSON格式存储数据。Mongoose是MongoDB的对象建模工具,提供了Schema定义、数据验证、查询构建等功能。
安装依赖 #
bash
npm install @nestjs/mongoose mongoose
npm install -D @types/mongoose
配置Mongoose #
基本配置 #
typescript
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/nest'),
],
})
export class AppModule {}
使用环境变量 #
typescript
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URI'),
useNewUrlParser: true,
useUnifiedTopology: true,
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
定义Schema #
基本Schema #
typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
@Schema()
export class User extends Document {
@Prop({ required: true })
name: string;
@Prop({ required: true, unique: true })
email: string;
@Prop({ required: true, select: false })
password: string;
@Prop({ default: true })
isActive: boolean;
@Prop({ type: Date, default: Date.now })
createdAt: Date;
@Prop({ type: Date, default: Date.now })
updatedAt: Date;
}
export const UserSchema = SchemaFactory.createForClass(User);
Schema选项 #
typescript
@Schema({
collection: 'users',
timestamps: true,
versionKey: false,
})
export class User extends Document {
// ...
}
属性装饰器选项 #
typescript
@Prop({
required: true,
unique: true,
lowercase: true,
trim: true,
minlength: 6,
maxlength: 100,
default: 'default value',
select: false,
index: true,
})
email: string;
嵌套文档 #
typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
class Address {
@Prop()
street: string;
@Prop()
city: string;
@Prop()
country: string;
}
@Schema()
export class User extends Document {
@Prop()
name: string;
@Prop({ type: Address })
address: Address;
}
export const UserSchema = SchemaFactory.createForClass(User);
关联关系 #
引用关系 #
typescript
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import * as mongoose from 'mongoose';
@Schema()
export class User extends Document {
@Prop()
name: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
@Schema()
export class Post extends Document {
@Prop({ required: true })
title: string;
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
author: mongoose.Schema.Types.ObjectId;
}
export const PostSchema = SchemaFactory.createForClass(Post);
填充引用 #
typescript
const post = await this.postModel
.findById(id)
.populate('author')
.exec();
注册Schema #
typescript
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [
MongooseModule.forFeature([
{ name: User.name, schema: UserSchema },
]),
],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
CRUD操作 #
注入Model #
typescript
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './schemas/user.schema';
@Injectable()
export class UsersService {
constructor(
@InjectModel(User.name) private userModel: Model<User>,
) {}
}
创建 #
typescript
async create(createUserDto: CreateUserDto): Promise<User> {
const createdUser = new this.userModel(createUserDto);
return createdUser.save();
}
async createMany(users: CreateUserDto[]): Promise<User[]> {
return this.userModel.insertMany(users);
}
查询 #
typescript
async findAll(): Promise<User[]> {
return this.userModel.find().exec();
}
async findOne(id: string): Promise<User | null> {
return this.userModel.findById(id).exec();
}
async findByEmail(email: string): Promise<User | null> {
return this.userModel.findOne({ email }).exec();
}
async findActive(): Promise<User[]> {
return this.userModel
.find({ isActive: true })
.sort({ createdAt: -1 })
.exec();
}
更新 #
typescript
async update(id: string, updateUserDto: UpdateUserDto): Promise<User | null> {
return this.userModel
.findByIdAndUpdate(id, updateUserDto, { new: true })
.exec();
}
async updateByEmail(email: string, data: any): Promise<User | null> {
return this.userModel
.findOneAndUpdate({ email }, data, { new: true })
.exec();
}
删除 #
typescript
async remove(id: string): Promise<User | null> {
return this.userModel.findByIdAndDelete(id).exec();
}
async deleteMany(ids: string[]): Promise<any> {
return this.userModel.deleteMany({ _id: { $in: ids } }).exec();
}
查询构建 #
条件查询 #
typescript
async search(query: any) {
return this.userModel
.find({
$or: [
{ name: { $regex: query.keyword, $options: 'i' } },
{ email: { $regex: query.keyword, $options: 'i' } },
],
})
.exec();
}
分页 #
typescript
async findWithPagination(page: number, limit: number) {
const skip = (page - 1) * limit;
const [data, total] = await Promise.all([
this.userModel.find().skip(skip).limit(limit).exec(),
this.userModel.countDocuments().exec(),
]);
return {
data,
total,
page,
limit,
totalPages: Math.ceil(total / limit),
};
}
聚合查询 #
typescript
async aggregateStats() {
return this.userModel.aggregate([
{ $match: { isActive: true } },
{ $group: { _id: '$role', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
]);
}
索引 #
定义索引 #
typescript
@Schema()
export class User extends Document {
@Prop({ index: true })
email: string;
@Prop({ index: true })
name: string;
}
UserSchema.index({ name: 1, email: 1 }, { unique: true });
文本索引 #
typescript
UserSchema.index({ name: 'text', bio: 'text' });
// 使用
const results = await this.userModel
.find({ $text: { $search: 'keyword' } })
.exec();
中间件 #
保存前中间件 #
typescript
UserSchema.pre<User>('save', function (next) {
if (this.isModified('password')) {
this.password = hashPassword(this.password);
}
this.updatedAt = new Date();
next();
});
查询后中间件 #
typescript
UserSchema.post<User>('find', function (docs) {
docs.forEach(doc => {
doc.password = undefined;
});
});
虚拟属性 #
typescript
UserSchema.virtual('fullName').get(function () {
return `${this.firstName} ${this.lastName}`;
});
// 启用虚拟属性
UserSchema.set('toJSON', { virtuals: true });
UserSchema.set('toObject', { virtuals: true });
事务处理 #
MongoDB 4.0+支持事务:
typescript
import { Injectable } from '@nestjs/common';
import { InjectConnection } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
@Injectable()
export class UsersService {
constructor(@InjectConnection() private connection: Connection) {}
async createWithTransaction(userData: any) {
const session = await this.connection.startSession();
session.startTransaction();
try {
const user = await this.userModel.create([userData], { session });
// 其他操作...
await session.commitTransaction();
return user;
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
}
GridFS文件存储 #
typescript
import { Injectable } from '@nestjs/common';
import { InjectConnection } from '@nestjs/mongoose';
import { Connection, Types } from 'mongoose';
import { GridFSBucket, GridFSBucketReadStream } from 'mongoose';
@Injectable()
export class FileService {
private bucket: GridFSBucket;
constructor(@InjectConnection() private connection: Connection) {
this.bucket = new GridFSBucket(this.connection.db);
}
async uploadFile(file: Express.Multer.File): Promise<Types.ObjectId> {
const uploadStream = this.bucket.openUploadStream(file.originalname, {
contentType: file.mimetype,
});
return new Promise((resolve, reject) => {
uploadStream.end(file.buffer);
uploadStream.on('finish', () => resolve(uploadStream.id));
uploadStream.on('error', reject);
});
}
async downloadFile(id: string): Promise<GridFSBucketReadStream> {
return this.bucket.openDownloadStream(new Types.ObjectId(id));
}
async deleteFile(id: string): Promise<void> {
await this.bucket.delete(new Types.ObjectId(id));
}
}
最佳实践 #
1. 使用DTO #
typescript
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}
2. 软删除 #
typescript
@Schema()
export class User extends Document {
@Prop()
name: string;
@Prop({ type: Date })
deletedAt: Date;
}
// 查询时排除已删除
async findActive() {
return this.userModel.find({ deletedAt: null }).exec();
}
3. 数据转换 #
typescript
UserSchema.set('toJSON', {
transform: (doc, ret) => {
ret.id = ret._id.toString();
delete ret._id;
delete ret.__v;
delete ret.password;
return ret;
},
});
总结 #
本章学习了NestJS与MongoDB的集成:
- Mongoose配置
- Schema定义
- CRUD操作
- 查询构建
- 索引和中间件
- 事务处理
接下来,让我们学习 Passport认证。
最后更新:2026-03-28