Nestjs-集成MongoDB数据库

1,119 阅读3分钟

Nest集成MongoDB数据库官网文档

前沿

Nestjs提供了两种方法集成MongoDB。一种是通过内置的TypeORM模块(内置有MongoDB连接器),另一种是利用Mongoose对MongoDB进行集成。本文主要实践第二种方法。

安装依赖

首先安装所需的依赖项:

npm install --save @nestjs/mongoose mongoose
连接数据库
方法一

首先是将MongooseModule导入到根模块AppModule中。使用MongooseModule.forRoot()方法,接收一个和Mongoose .connect()相同的配置对象,如下所示:

@@filename(app.module)
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [MongooseModule.forRoot(`mongodb://${account}:${password}@${host}:${port}/${db}`)]
})
export class AppModule {}

查看MongooseModule类的定义,forRoot(uri, options?)还接收一个options参数,这个配置参数支持配置Mongodb的其他配置项。

nest-mongo-1.jpg

可以看下它们的继承关系:

nest-mongo-2.jpg

nest-mongo-3.jpg

所以,一些常用的配置都可以在这里完成。

方法二:异步获取配置

当我们需要异步获取配置信息的时候,可以使用forRootAsync()方法,传入一个工厂函数useFactory()获取配置,如下:

MongooseModule.forRootAsync({
  useFactory: async () => {
    // 一些异步获取操作
    let uri = `mongodb://${account}:${password}@${host}:${port}/${db}`;
    return { uri };
  },
});

也可以通过inject注入依赖项,此处是通过Nest内置的ConfigModule模块将配置信息提取,统一管理:

MongooseModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    uri: configService.get<string>('MONGODB_URI'),
  }),
  inject: [ConfigService],
});
定义schema

我们都知道每个Schema都映射到一个MongoDB的集合,负责从底层MongoDB数据库创建和读取文档。接下来使用Nestjs装饰器创建数据模型Schema。

创建user.schema.ts文件,配置如下:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
// Schema装饰器同样接受一个options参数,让我们按意愿配置数据模型
// 同new mongoose.Schema(_, options)中的options配置一样,具体详情可查阅mongoose文档
@Schema({
  	// 配置集合名称。如果不配置此项的话,默认会取类名并加上一个's',数据库内的集合名称就是'users'
    collection: 'user', 
    // timestamps是用来配置createdAt和updatedAt的。默认为false不创建,可按需求自定义
    timestamps: {
        createdAt: 'time',
        updatedAt: false
    },
    // 这里是对toJSON()方法进行配置,toJSON()方法是用来把查询出来的数据库数据对象转换成普通对象
  	// 数据库默认都有一个_id,是ObjectId类型,同时为其制定一个虚拟映射id,string类型
  	// virtuals是控制id字段是否显示的,默认为false
  	// transform是对结果文档做一些操作,常见的就是去掉_id,只保留id
    toJSON: {
        virtuals: true,
        transform: (doc, ret, opt) => {
            delete ret._id;
        }
    }
})
export class User {
  	// Prop装饰器是用来配置属性的,比如定义属性类型,是否必填等,具体可参考文档
    @Prop({ required: true })
    name: string;
    @Prop({ required: true })
    pwd: string;
    @Prop({ required: true })
    mobile: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
export type UserDocument = User & Document;
注入schema

定义好Schema后,在需要使用的模块注入,比如在UserModule中:

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { User, UserSchema } from './schemas/user.schema';

@Module({
  // 在这个模块中,可能会操作数据库的很多集合,把需要的Schema注入到这里
  imports: [MongooseModule.forFeature([
    { name: User.name, schema: UserSchema }
    // ...
  ])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

然后在需要操作数据库的地方注入UserModel进行数据库操作,比如UserService

import { Model } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { User, UserDocument } from './schemas/user.schema';

@Injectable()
export class UserService {
  constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}

  async create(createUserDto): Promise<User> {
    const user = await (await this.userModel.create(createUserDto)).toJSON();
    return user;
  }
}
钩子函数

当我们想注册像prepost这样的钩子函数的时候,需要在Schema注册之前,注册钩子。因为在编译Schema后调用pre()post()在Mongoose中不起作用。所以需要使用forFeatureAsync()方法以及工厂函数useFactory(),使用方法如下:

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { User, UserSchema } from './schemas/user.schema';

@Module({
  // 在这个模块中,可能会操作数据库的很多集合,把需要的Schema注入到这里
  imports: [MongooseModule.forFeatureAsync([
    { 
      name: User.name, 
      useFactory: () => {
        const schema = UserSchema;
        schema.pre('save', function() { // 一些操作 });
        return schema; // 最后一定要返回schema,否则注入无效
    	}
    },
    // ...
  ])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
总结

以上内容官网均有涉及,浅浅记录,不值一提。如有错误,欢迎指正!!!