Skip to content

MongoDB(Mongoose)

警告

在本文中,你将学习如何基于 Mongoose 包,通过自定义组件从零创建一个 DatabaseModule。因此,这种方案会包含较多样板与额外负担,而这些内容实际上可以通过现成可用的 @nestjs/mongoose 包省略掉。要了解更多内容,请参见这里

Mongoose 是最流行的 MongoDB 对象建模工具。

入门

要开始使用这个库,我们需要安装所有必需依赖:

typescript
$ npm install --save mongoose

第一步是使用 connect() 函数与数据库建立连接。connect() 函数会返回一个 Promise,因此我们需要创建一个异步提供者

typescript
// database.providers.ts
import * as mongoose from 'mongoose';

export const databaseProviders = [
  {
    provide: 'DATABASE_CONNECTION',
    useFactory: (): Promise<typeof mongoose> =>
      mongoose.connect('mongodb://localhost/nest'),
  },
];

提示

遵循最佳实践,我们将自定义 provider 声明在一个独立文件中,并使用 *.providers.ts 后缀。

然后,我们需要导出这些 provider,以便应用的其他部分也能访问它们。

typescript
// database.module.ts
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';

@Module({
  providers: [...databaseProviders],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

现在我们就可以使用 @Inject() 装饰器注入 Connection 对象。任何依赖 Connection 异步 provider 的类,都会等待对应的 Promise 被解析。

注入模型

在 Mongoose 中,一切都源自 Schema。先来定义 CatSchema

typescript
// schemas/cat.schema.ts
import * as mongoose from 'mongoose';

export const CatSchema = new mongoose.Schema({
  name: String,
  age: Number,
  breed: String,
});

CatsSchema 属于 cats 目录,而该目录代表 CatsModule

现在该创建一个 Model provider 了:

typescript
// cats.providers.ts
import { Connection } from 'mongoose';
import { CatSchema } from './schemas/cat.schema';

export const catsProviders = [
  {
    provide: 'CAT_MODEL',
    useFactory: (connection: Connection) => connection.model('Cat', CatSchema),
    inject: ['DATABASE_CONNECTION'],
  },
];

警告

在真实应用中,应避免使用魔法字符串CAT_MODELDATABASE_CONNECTION 都应该放在单独的 constants.ts 文件中。

现在我们就可以在 CatsService 中使用 @Inject() 装饰器注入 CAT_MODEL

typescript
// cats.service.ts
import { Model } from 'mongoose';
import { Injectable, Inject } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
import { CreateCatDto } from './dto/create-cat.dto';

@Injectable()
export class CatsService {
  constructor(
    @Inject('CAT_MODEL')
    private catModel: Model<Cat>,
  ) {}

  async create(createCatDto: CreateCatDto): Promise<Cat> {
    const createdCat = new this.catModel(createCatDto);
    return createdCat.save();
  }

  async findAll(): Promise<Cat[]> {
    return this.catModel.find().exec();
  }
}

在上面的例子中,我们使用了 Cat 接口。这个接口继承自 mongoose 包中的 Document

typescript
import { Document } from 'mongoose';

export interface Cat extends Document {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}

数据库连接是异步的,但 Nest 让整个过程对最终用户几乎完全透明。CatModel 类会等待数据库连接完成,而 CatsService 也会延迟到 model 准备就绪之后才实例化。整个应用会在所有类都完成实例化后再启动。

下面是最终的 CatsModule

typescript
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { catsProviders } from './cats.providers';
import { DatabaseModule } from '../database/database.module';

@Module({
  imports: [DatabaseModule],
  controllers: [CatsController],
  providers: [
    CatsService,
    ...catsProviders,
  ],
})
export class CatsModule {}

提示

别忘了在根 AppModule 中导入 CatsModule

示例

一个可运行的示例见这里

基于 NestJS 官方文档翻译