ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Midway MongoDB 接入指南:基于 Typegoose 与 Mongoose 的完整实战方案

Midway MongoDB 接入指南:基于 Typegoose 与 Mongoose 的完整实战方案 后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载本文是 Midway 框架中 MongoDB 数据访问的实战指南围绕框架的midwayjs/typegoose与midwayjs/mongoose两个组件系统讲解从依赖选型、组件安装、连接配置到实体定义、数据读写与多库切换的完整链路并补充源码级的实现原理说明。读完本文你将掌握在 Midway 标准项目、Serverless 与一体化项目中接入 MongoDB 的两种推荐姿势Typegoose 装饰器模式与直接使用 Mongoose并能独立处理常见的版本兼容问题。说明本文对应的官方文档 site/docs/legacy/mongodb.md 自 Midway v3.4.0 版本起被标记为废弃但其中关于组件配置、版本选型与使用方式的讲解仍然适用于相应版本的历史项目也是理解当前 Mongoose/Typegoose 组件底层机制packages/mongoose/src 与 packages/typegoose/src的重要参考。本文以该文档为主线结合仓库源码展开。为什么选择 Typegoose 与 Mongoose在这一章节中官方文档选择 Typegoose 作为基础的 MongoDB ORM 库。正如其自身描述的那样 Define Mongoose models using TypeScript classesTypegoose 与 TypeScript 的结合十分自然。简单来说Typegoose 是使用 TypeScript 编写 Mongoose 模型的包装器它的大部分能力仍然由 Mongoose 库提供。你完全可以把它理解为用类装饰器简化 Mongoose Schema/Model 的创建过程用prop()装饰器声明字段替代手写Schema对象用EntityModel()装饰器注册实体替代mongoose.model()调用通过InjectEntityModel()注入已注册的 Model由框架负责连接绑定与实例管理。如果不想引入 Typegoose 的抽象也可以直接选择 Mongoose 库使用文档对两条路线都有完整描述。两条路线均可用于标准项目、Serverless 与一体化前后端一体项目描述支持情况可用于标准项目✅可用于 Serverless✅可用于一体化✅Mongoose 版本依赖Mongoose 和你服务器使用的 MongoDB Server 版本之间存在对应关系请务必注意版本不匹配可能导致连接异常或 API 行为差异。MongoDB Server 2.4.xmongoose ^3.8 或 4.xMongoDB Server 2.6.xmongoose ^3.8.8 或 4.xMongoDB Server 3.0.xmongoose ^3.8.22、4.x 或 5.xMongoDB Server 3.2.xmongoose ^4.3.0 或 5.xMongoDB Server 3.4.xmongoose ^4.7.3 或 5.xMongoDB Server 3.6.xmongoose 5.xMongoDB Server 4.0.xmongoose ^5.2.0MongoDB Server 4.2.xmongoose ^5.7.0MongoDB Server 4.4.xmongoose ^5.10.0MongoDB Server 5.xmongoose ^6.0.0Mongoose 相关的依赖比较复杂且对应不同的版本现阶段官方主要使用的是 mongoose v5 和 v6。从mongoosev5.11.0开始Mongoose 官方支持了类型定义因此不再需要安装types/mongoose依赖包。安装包依赖组合支持 MongoDB Server 5.x推荐组合{ dependencies: { mongoose: ^6.0.7, typegoose/typegoose: ^9.0.0 } }typegoose/typegoose仅在需要使用 Typegoose 时才需要安装。支持 MongoDB Server 4.4.x以下版本不需要安装额外定义包{ dependencies: { mongoose: ^5.13.3, typegoose/typegoose: ^8.0.0 } }以下版本需要安装额外定义包不推荐{ dependencies: { mongodb: 3.6.3, mongoose: ~5.10.18, typegoose/typegoose: ^7.0.0 }, devDependencies: { types/mongodb: 3.6.3, types/mongoose: ~5.10.3 } }注意mongodb这里被写死为 3.6.3是因为该版本下 Mongoose 内部锁定了该版本types/mongodb同样只能使用 3.6.3types/mongoose则需匹配~5.10.3。其余的 MongoDB 安装模块组合类似官方并未逐一测试。使用 Typegoose 接入 MongoDB1、安装组件安装 Typegoose 组件即可获得访问 MongoDB 的能力$ npm i midwayjs/typegoose4 --save请务必注意先查看上文版本依赖小节提前编写/安装 mongoose 等相关依赖包。或者在package.json中增加如下依赖后重新安装{ dependencies: { midwayjs/typegoose: ^4.0.0 } }安装后需要手动在src/configuration.ts中启用组件代码如下// configuration.ts import { Configuration } from midwayjs/core; import * as typegoose from midwayjs/typegoose; Configuration({ imports: [ typegoose // 加载 typegoose 组件 ], importConfigs: [ join(__dirname, ./config) ] }) export class MainConfiguration { }从源码看Typegoose 组件本身并不重复实现连接管理而是直接imports了 Mongoose 组件packages/typegoose/src/configuration.ts其命名空间为typegoose。正如文档所述在该组件中midway 只是做了简单的配置规则化并将其注入到初始化流程中。2、配置连接信息在src/config/config.default.ts中加入连接配置export default { // ... mongoose: { client: { uri: mongodb://localhost:27017/test, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } } }, }这里uri是 MongoDB 连接串options中useNewUrlParser与useUnifiedTopology是 MongoDB Node Driver 的旧版连接器选项user/pass用于认证。补充说明client单库与clients多库属于旧版配置格式。在 packages/mongoose/src/configuration.ts 中框架的importConfigFilter会在配置加载时自动把client转换为dataSource.default、把clients整体转换为dataSource因此旧格式仍然可用而在 packages/mongoose/src/manager.ts 中也会对这两种写法输出弃用警告并做同样的转换。新格式推荐直接使用dataSourceexport default { // ... mongoose: { dataSource: { default: { uri: mongodb://localhost:27017/test, options: { /* ... */ } } } }, }3、简单的目录结构以一个简单的项目为例其他结构请自行参考MyProject ├── src // TS 根目录 │ ├── config │ │ └── config.default.ts // 应用配置文件 │ ├── entity // 实体数据库 Model目录 │ │ └── user.ts // 实体文件 │ ├── configuration.ts // Midway 配置文件 │ └── service // 其他的服务目录 ├── .gitignore ├── package.json ├── README.md └── tsconfig.json数据库实体主要放在entity目录非强制这只是一个简单的约定便于与 service 等目录区分。4、创建实体文件import { prop } from typegoose/typegoose; import { EntityModel } from midwayjs/typegoose; EntityModel() export class User { prop() public name?: string; prop({ type: () [String] }) public jobs?: string[]; }等价于使用 Mongoose 的下列代码const userSchema new mongoose.Schema({ name: String, jobs: [{ type: String }] }); const User mongoose.model(User, userSchema);所以说Typegoose 只是简化了 Model 的创建过程。从实现上看EntityModel()装饰器做两件事通过DecoratorManager.saveModule(ENTITY_MODEL_KEY, target)注册模块并通过MetadataManager.defineMetadata保存连接名等选项packages/typegoose/src/decorator/entityModel.ts其中的元数据键为TYPEGOOSE:MODELpackages/typegoose/src/interface.ts。5、引用实体调用数据库示例代码如下import { Provide } from midwayjs/core; import { InjectEntityModel } from midwayjs/typegoose; import { ReturnModelType } from typegoose/typegoose; import { User } from ../entity/user; Provide() export class TestService { InjectEntityModel(User) userModel: ReturnModelTypetypeof User; async getTest(){ // create data const { _id: id } await this.userModel.create({ name: JohnDoe, jobs: [Cleaner] } as User); // an as assertion, to have types for all properties // find data const user await this.userModel.findById(id).exec(); console.log(user) } }InjectEntityModel(User)的本质是通过DecoratorManager.createCustomPropertyDecorator(ENTITY_MODEL_KEY, { modelKey })注册一个自定义属性注入器packages/typegoose/src/decorator/injectEntityModel.ts在组件onReady阶段框架会遍历所有数据源并调用getModelForClass(Model, { existingConnection: conn })把实体类与连接绑定生成 Model随后存入内部 Mappackages/typegoose/src/configuration.ts注入时直接从该 Map 中取出返回。6、多库的情况首先配置多个连接。在src/config/config.default.ts中加入连接配置default代表了默认的连接export default { // ... mongoose: { clients: { default: { uri: mongodb://localhost:27017/test, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } }, db1: { uri: mongodb://localhost:27017/test1, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } } } }, }定义实体时使用固定的连接EntityModel() // 默认使用了 default 连接 class User { prop() public name?: string; prop({ type: () [String] }) public jobs?: string[]; } EntityModel({ connectionName: db1 // 这里使用了 db1 连接 }) class User2 { prop() public name?: string; prop({ type: () [String] }) public jobs?: string[]; }在使用时注入特定连接的 ModelProvide() export class TestService { InjectEntityModel(User) userModel: ReturnModelTypetypeof User; InjectEntityModel(User2) user2Model: ReturnModelTypetypeof User2; async getTest(){ const { _id: id } await this.userModel.create({ name: JohnDoe, jobs: [Cleaner] } as User); // an as assertion, to have types for all properties const user await this.userModel.findById(id).exec(); console.log(user) const { _id: id2 } await this.user2Model.create({ name: JohnDoe, jobs: [Cleaner] } as User2); // an as assertion, to have types for all properties const user2 await this.user2Model.findById(id2).exec(); console.log(user2) } }从源码可以确认connectionName的取值逻辑EntityModel()未传参数时框架默认使用default连接名若指定的连接名在数据源工厂中不存在会直接抛出connection name xxx not found错误packages/typegoose/src/configuration.ts。另外源码中还存在一个legacyMode兼容分支当项目里没有任何EntityModel()实体、且配置中直接存在uri字段时框架会降级为直接调用mongoose.connect(uri, options)全局连接同文件 L83-L89并在应用停止时disconnect()。直接使用 MongooseMongoose 组件是 Typegoose 的基础组件有时候我们可以直接使用它绕开实体装饰器按传统方式创建 Schema 与 Model。1、安装组件请务必注意先查看版本依赖小节提前编写/安装 mongoose 等相关依赖包。$ npm i midwayjs/mongoose --save或者在package.json中增加如下依赖后重新安装{ dependencies: { midwayjs/mongoose: ^4.0.0 } }2、开启组件安装后需要手动在src/configuration.ts配置代码如下// configuration.ts import { Configuration } from midwayjs/core; import * as mongoose from midwayjs/mongoose; Configuration({ imports: [ mongoose // 加载 mongoose 组件 ], importConfigs: [ join(__dirname, ./config) ] }) export class MainConfiguration { }3、配置和 Typegoose 相同或者说 Typegoose 使用的就是 Mongoose 的配置。单库export default { // ... mongoose: { client: { uri: mongodb://localhost:27017/test, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } } }, }多库export default { // ... mongoose: { clients: { default: { uri: mongodb://localhost:27017/test, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } }, db1: { uri: mongodb://localhost:27017/test1, options: { useNewUrlParser: true, useUnifiedTopology: true, user: ***********, pass: *********** } } } }, }无论哪种格式底层都由MongooseDataSourceManager通过mongoose.createConnection(config.uri, config.options)创建连接packages/mongoose/src/manager.ts并监听error、disconnected、connected、reconnected事件输出对应日志若配置中带有entities字段还会把实体类挂到连接对象上供上层消费。健康检查逻辑则通过onHealthCheck遍历各数据源任一数据源未就绪即返回失败原因packages/mongoose/src/configuration.ts。4、使用在只有一个默认连接或者直接使用 default 连接时可以直接使用封装好的MongooseConnectionService对象来创建 Modelimport { Provide, Inject } from midwayjs/core; import { MongooseConnectionService } from midwayjs/mongoose; import { Schema, Document } from mongoose; interface User extends Document { name: string; email: string; avatar: string; } Provide() export class TestService { Inject() conn: MongooseConnectionService; async invoke(){ const schema new SchemaUser({ name: { type: String, required: true }, email: { type: String, required: true }, avatar: String }); const UserModel this.conn.modelUser(User, schema); const doc new UserModel({ name: Bill, email: billinitech.com, avatar: https://i.imgur.com/dM7Thhn.png }); await doc.save(); } }从源码看MongooseConnectionService是mongoose.Connection接口的代理实现初始化时从数据源管理器取出default连接若不存在则抛出mongoose default instance not found错误其余model、collection、startSession、transaction、watch等方法通过delegateTargetMethod委托给真实连接packages/mongoose/src/manager.ts。注意该服务及下面的工厂类在源码中均标注了deprecated新代码更推荐直接通过数据源管理器或InjectEntityModel使用。如果配置了多个其他连接请从工厂方法中获取连接后再使用import { MongooseConnectionServiceFactory } from midwayjs/mongoose; import { Schema } from mongoose; Provide() export class TestService { Inject() connFactory: MongooseConnectionServiceFactory; async invoke(){ // get db1 connection const conn this.connFactory.get(db1); // get default connection const defaultConn this.connFactory.get(default); } }MongooseConnectionServiceFactory内部委托给MongooseDataSourceManagerget(id)返回指定名称的连接实例has(id)判断连接是否存在createInstance(config, clientName)可按需动态创建新连接packages/mongoose/src/manager.ts。常见问题1、E002: You are using a NodeJS Version below 12.22.0在新版本typegoose/typegoosev8、v9中增加了 Node 版本的校验如果你的 Node.js 版本低于 v12.22.0就会出现这个提示。普通情况下请升级 Node.js 到这个版本以上即可解决。在特殊场景下比如 Serverless 无法修改 Node.js 版本且版本低于 v12.22 的情况下由于 v12 版本子版本其实都可以可以通过临时修改process.version绕过// src/configuration.ts Object.defineProperty(process, version, { value: v12.22.0, writable: true, }); // other code export class MainConfiguration {}小结围绕 site/docs/legacy/mongodb.md 这篇文档本文完整覆盖了 Midway 接入 MongoDB 的两条路径Typegoose 装饰器路径安装midwayjs/typegoose→ 在configuration.ts引入组件 → 配置mongoose连接 → 用EntityModel()/prop()定义实体 → 用InjectEntityModel()注入 Model 执行 CRUD多库场景通过connectionName绑定连接Mongoose 原生路径安装midwayjs/mongoose→ 引入组件 → 配置连接 → 注入MongooseConnectionService默认库或MongooseConnectionServiceFactory多库创建 Schema 与 Model。同时结合仓库源码packages/typegoose/src、packages/mongoose/src解释了组件之间的依赖关系、配置的兼容转换client/clients→dataSource、实体与连接的绑定原理以及健康检查机制方便你在遇到版本或配置问题时快速定位根因。核心要点先按 MongoDB Server 版本锁定 Mongoose 依赖组合再决定使用装饰器还是原生写法最后注意 Node 版本下限即可。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Kornia 边缘检测全指南Canny、Sobel、Laplacian 与空间梯度算子源码级解析Kornia 边缘检测全指南Canny、Sobel、Laplacian 与空间梯度算子源码级解析 本指南系统讲解 Kornia 几何计算机视觉库 kornia后端微服务云原生408考研四阶段打法一套资源库跑完四门课408考研四阶段打法一套资源库跑完四门课 翻开数据结构复习指导的目录8章计组7章操作系统5章计网6章。26个章节排在你面前先啃哪个都说不清。我开始备文档教育教程Mongoose MongoDB ODM 实战指南Schema、Model、连接与中间件的完整使用手册Mongoose MongoDB ODM 实战指南Schema、Model、连接与中间件的完整使用手册 Mongoose 是为异步环境设计的 MongoDB数据库后端上一篇Angel 分布式 KMeans 聚类实战指南从 Mini-batch 原理到 Yarn 集群训练与预测下一篇飞书 Python SDK 上手指南一条命令跑通你的第一个 API 调用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表