NestJS Logo
NestJS 中文文档
v10.0.0
  • 介绍
  • 快速上手
  • 控制器
  • 提供者
  • 模块
  • 中间件
  • 异常过滤器
  • 管道
  • 守卫
  • 拦截器
  • 自定义装饰器
  • 自定义提供者
  • 异步提供者
  • 动态模块
  • 依赖注入作用域
  • 循环依赖
  • 模块引用
  • 懒加载模块
  • 执行上下文
  • 生命周期事件
  • 发现服务
  • 跨平台无关性
  • 测试
迁移指南
API 参考
官方课程
  1. 文档
  2. 实用案例
  3. 数据库 ORM
  4. Mongoose

TypeORM
SQL(Sequelize)

Mongoose(MongoDB)

注意

本文将向你展示如何基于 Mongoose 包,从零开始构建一个自定义 DatabaseModule。请注意,这种方法会涉及较多的样板代码。作为替代方案,你可以使用官方提供的 @nestjs/mongoose 包来极大地简化这个过程。我们强烈建议你优先考虑使用该包,具体用法请参见此章节。

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

快速开始

首先,安装所需的依赖项:

$ npm install mongoose

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

database.providers.ts
import * as mongoose from 'mongoose'

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

按照最佳实践,建议将自定义提供者声明在独立的文件中,并以 .providers.ts 作为文件名后缀。

接下来,需要导出这些提供者,以便它们能在应用的其他部分被注入和使用。

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

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

现在,你就可以通过 @Inject() 装饰器注入 Connection 对象了。Nest 会确保任何依赖于此异步提供者的类,都在 Promise 解析完毕后才进行实例化。

模型注入(Model injection)

在 Mongoose 中,一切都围绕 Schema 展开。先来定义一个 CatSchema:

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

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

CatSchema 位于 cats 目录,该目录用于存放 CatsModule 的所有相关文件。

接着,来创建一个模型提供者:

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_MODEL 和 DATABASE_CONNECTION 等注入令牌(token)统一放在独立的 constants.ts 文件中管理。

现在,就可以通过 @Inject() 装饰器将 CAT_MODEL 注入 CatsService 了:

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 类型:

interfaces/cat.interface.ts
import { Document } from 'mongoose'

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

数据库连接是异步的,但 Nest 会让这个过程对开发者透明。CatModel 的提供者在注入前会等待数据库连接完成,而 CatsService 也会相应地延迟实例化,直到模型提供者就绪。整个应用会在所有提供者都完成初始化后才启动。

下面是最终的 CatsModule:

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 {}
提示
不要忘记将 CatsModule 导入到根模块(即 AppModule)中。

示例

本章的完整示例可以在此处查看。