ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.
class User extends Typegoose {
@prop()
name?: string;
}
const UserModel = new User().getModelForClass(User);
// UserModel is a regular Mongoose Model with correct types
(async () => {
const u = await UserModel.create({ name: 'JohnDoe' });
const user = await UserModel.findOne();
console.log(user);
// prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
})();
typegoose/typegoose
class User {
@prop()
public name?: string;
}
const UserModel = getModelForClass(User);
// UserModel is a regular Mongoose Model with correct types
(async () => {
const { _id: id } = await UserModel.create({ name: 'JohnDoe' } as User); // an "as" assertion, to have types for all properties
const user = await UserModel.findById(id).exec();
console.log(user);
// prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
})();
两者差别,一个是类继承,一个是函数。
两个目的都是一样,正如它们文档介绍那样:Typegoose - Define Mongoose models using TypeScript classes.
static get schema() 神奇的地方就在这里,typegoose暴露诸如getModelForClass()和buildSchema()之类的函数,我们只需要创建纯就可以通过这些方法和typegoose做绑定关联。为什么需要buildSchema(),那是因为我们使用@nestjs/mongoose,通过MongooseModule.forFeature注册mongoose.model,需要2个必须属性:name和Schema。
这里buildSchema()就是生成Schema的方法。它的工作原理是我们调用buildSchema()并传入this。在这种情况下,在静态方法中,实际的类本身调用schema getter,这使得将get schema()放在抽象类上成为可能,这样我们就可以在这里通用处理一下。在此之前,我们需要编写方法来获取每个领域模型类的模式和模型名,这有点像样板文件。
static get modelName() 就很简单。我们只返回this.name,并且这个,同样在静态方法的上下文中,指向实际的类,所以this.name返回类名。然而,如果你持怀疑态度,你可能想要返回一些其他的东西,或者只是有一个函数,它会为你的MongooseModel返回一些有意义的名字。我倾向于在这里返回this.name,因为我在更多的地方使用this.name,比如swag UI来标注tags
今天,我将与你分享在使用NestJS和MongoDB时一直在使用的工作流程/技术。 此工作流程利用了Typegoose的功能。
Typeorm的简介,虽说支持很多数据库,MongoDB也是支持的,还有一篇专门介绍兼容MongoDB的文档,我在另外一个项目使用了一下,发现支持的并不是那么好,这也难怪没有写
Supports
。需要寻找一个替代品,谷歌搜索找到了Typegoose
。关于这2个版本有什么不同呢?
基本差别不大,
szokodiakos/typegoose
的暂停维护,查看原因,typegoose/typegoose
相当一个分支,不过两者写法还是有点不同。szokodiakos/typegoose
typegoose/typegoose
两者差别,一个是类继承,一个是函数。
两个目的都是一样,正如它们文档介绍那样:
Typegoose - Define Mongoose models using TypeScript classes.
它们出现也是正如前面的困惑一样。介绍这么多,该进入正题了。
让我们开始吧。
首先使用@nestjs/cli初始化一个新的NestJS应用程序
接下来,让我们安装依赖项:
vs code 打开项目
然后,删除
app.controller.ts
和app.service.ts
。修改你的app.module.ts
使用
@nestjs/mongoose
连接我们的Mongo连接。app.module.ts
现在,你可以尝试运行服务器:
你应该会看到以下内容:
我习惯把数据库管理单独放在一起,这样便于管理,也利于分层,在写法上,不会出现循环依赖的问题。虽然nest有解决方法,但是能避免就应该避免一下。
打开
base.model.ts
然后输入以下内容先来理解一下上面的代码:
created_at
,`updated_at和id是我所有领域模型的三个字段(你还可以添加更多通用属性或Schema字段)。timestamps启用映射我们创建时间和更新时间自动更新。id实际上是_id的一个getter,所以它总是在那里,但是为了能够在与.lean()或.toJSON()匹配时获取id,我们需要设置toJSON:{…}选项,如代码所示@prop()
将字段注释为Schema的一部分。在typegoose了解更多static get schema()
神奇的地方就在这里,typegoose暴露诸如getModelForClass()
和buildSchema()
之类的函数,我们只需要创建纯就可以通过这些方法和typegoose做绑定关联。为什么需要buildSchema()
,那是因为我们使用@nestjs/mongoose
,通过MongooseModule.forFeature
注册mongoose.model
,需要2个必须属性:name
和Schema
。 这里buildSchema()
就是生成Schema
的方法。它的工作原理是我们调用buildSchema()并传入this。在这种情况下,在静态方法中,实际的类本身调用schema getter,这使得将get schema()放在抽象类上成为可能,这样我们就可以在这里通用处理一下。在此之前,我们需要编写方法来获取每个领域模型类的模式和模型名,这有点像样板文件。static get modelName()
就很简单。我们只返回this.name
,并且这个,同样在静态方法的上下文中,指向实际的类,所以this.name
返回类名。然而,如果你持怀疑态度,你可能想要返回一些其他的东西,或者只是有一个函数,它会为你的MongooseModel
返回一些有意义的名字。我倾向于在这里返回this.name
,因为我在更多的地方使用this.name
,比如swag UI
来标注tags
现在我们有了基础模型,让我们来处理基础服务。打开base.repository.ts并粘贴以下代码。但是在显示代码之前,我想解释一下。
什么是BaseRepository ?BaseRepository是Repository模式。然而,使用像mongoose这样的ODM,我感觉MongooseModel已经有点像一个存储库了。完全可以去掉存储库层,以减少整个应用程序中抽象的数量。同样,这取决于应用程序需求的特征。我只是想让大家明白我的观点,并解释我为什么要这么做。还有一个好处减少循环依赖。现在我们已经清楚了,让我们继续:
这个代码很长。我们在说明一下做了什么:
T extends BaseModel
。这就是TypeScript的高级类型。在这里,我们明确地说T extends BaseModel
,这样我们只能将实际的领域模型类传递给这个基本服务,这是一个安全行为,你可以通过TypeScript来防止将ANY作为类型参数传递。model
的受保护字段,其类型为ReturnModelType<AnyParamConstructor<T>>
。 实际上,ReturnModelType<AnyParamConstructor<T>>
只是mongoose.model()
将返回的类型。 那么是不是Model<T>
?是的。 但是Model<T>
期望T extends mongoose.Document
的接口。 使用typegoose,这里的所有内容都是类,因此我们无法真正使用Model提供的方法。 另一个想法是使模型受到保护,这意味着只有子类才能访问此字段,因此我们不会在应用程序的任何其他层(例如控制器层)中公开
userService.model`mongoose.Model
的方法并返回适当的类型。你可能已经注意到,每种方法都有两个版本。 第一个版本返回一个DocumentQuery,它使你可以链接方法以进一步:filter
,project
, 和一些其他东西,比如populate
或lean
。 第2版(异步版)可在你不关心任何其他可链接方法而只想快速获取数据的情况下提供帮助。异步版本还将具有错误处理程序,我们将在其中引发MongoError
的InternalServerErrorException
。 使用toObjectId
将字符串转换成Types.ObjectId
类型。现在有基础模型和集成服务,我们来创建一个用户模型:
打开user.model.ts
@prop(options: object)
mongoose
配置一样打开user.repository.ts
我只需要继承
BaseRepository
,书写特定的快捷方法即可,并导出UserEntity
和UserModel
类型,模型名称userModelName
。接下来打开user.module.ts
这里重要的一点是我们调用
MongooseModule.forFeature
并传递模型数组。MongooseModule.forFeature
并将获取当前的mongoose.Connection
并添加传入的模型,然后将这些模型提供给NestJS的IoC容器(用于依赖注入)。 现在,你可以看到schema和modelName很重要,并且BaseModel在这里有很大帮助。导出索引index.ts
现在我们在业务模块auth里面使用
UserModelModule
完成注册登出等操作AuthService中使用UserRepository:
即使这只是一个最小的示例,但我希望它向你展示如何利用TypeScript和Typegoose提取一些基础知识以加快开发过程。 此外,你甚至可以拥有一个BaseController,该Controller拥有一个受保护的baseService,它将涵盖您的基本CRUD功能。 基本就已经完结了,为了这个东西我花了很多时间和经历去挖坑,这里记录一下挖坑总结,这原因是typegoose的完整的栗子太少。
完整栗子:传送门
今天就到这里吧,伙计们,玩得开心,祝你好运