Nestjs + MongoDB 在内部低代码平台的落地实践

前言

NodeJs在前端领域的作用不言而喻,做为前端的“基石”,在打包工具、脚手架、前端工程中间层(模板渲染、接口转发)、Server端中应用广泛,掌握NodeJs开发技能已经是每个前端开发者的必备技能之一,今天这篇文章,就介绍一下,内部自研项目使用NodeJs全栈开发的落地经历。

背景

在低代码平台的浪潮中,我司也自研了自己的低代码平台,其中,前端采用可视化拖拽引擎 + 解析器的模式,以jsonSchema为媒介,来实现低代码开发模式,这里就需要一个稳定的Server端来支持其持久化数据存储以及其平台业务流转。我们采用了NestJs + MongoDB的技术方案,下面是一个简单的架构图,一些技术细节不便公开,所以web层的技术架构简写,本文我们只介绍Server端


其中

  • 数据库使用MongoDB
  • Server端核心框架使用NestJs
  • oa登录使用内部express中间件
  • 前端部署使用node模板
  • 对接第三方(如:权限系统)使用scf

技术选型

🤔 为什么是NestJs?

首先,nestjs是基于express的一款企业级应用框架(默认express,还可支持Fastify,本文只谈express),nodejs的完整框架有很多,如expresskoa2eggjs等。但我们选择了nestjs,其主要原因是内部生态;我们知道,expresskoa2中间件的基本语法不同,express是基于promise的,而koa2是基于async/await的,所以,express中间件和koa2中间件是不通用的。而团队内部的沉淀的中间件(如:登录中间件)都是express生态,这是我们选择nestjs最主要原因;另外,nestjs的周边生态丰富、并完全支持typescript,于是我们坚定的使用了nestjs。

🤔为什么是MongoDB?

行业内有很多优秀的数据库,如MySql等,但我们选择了MongoDB,其主要原因是,相对简单灵活且轻量,因为我们的低代码平台是一个专注于辅助开发的功能性平台,非服务型产品,所以在数据性能方面没有太苛刻的要求,同时,因为我们的所有开发人员为FE,考虑到”易上手“的实际因素,而nestjs对MongoDB的支持又很完善(@nestjs/mongoose),

简述NestJs是如何运行的?

1️⃣ 入口文件
和所有Nodejs框架一样,NestJs也是需要一个入口文件,来完成一个“服务”的初始化,我们需要用到Nestjs为我们提供的工厂函数。于是,一个Nestjs的入口文件,大概是这样的

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create<NestExpressApplication>(AppModule);
    // ...
    // 各种express或者nest中间件
    // ...
    await app.listen(8001);
}
module.exports = bootstrap;

2️⃣ 模块加载
从上面的初始化代码,我们可以看到,除了两个nestjs自带工厂函数以及泛型外,还有一个我们自己的文件“app.module”。
这里不得不说一下,Nest的本质,是一个MVC框架,而在Nestjs中,"M"更倾向于“模块”的概念;所以,上面的初始化代码,可以理解为整个项目的“模块加载”,而NestJs又采用了“装饰器”的语法来对类做修饰;下面来看一下,Nestjs如何加载全部模块,以及在全局应用某些逻辑

import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { Controller } from './app.controller'

@Module({
  imports: [
    // 在这里引入其他模块,以及数据库相关配置
  ],
  controllers: [Controller], // 这里可作为全局的Controller
  providers: [
    // 依赖注入;全局注入一些依赖逻辑,如:全局响应拦截器等,其中依赖注入的逻辑,Nest会帮我们处理好模块之间的依赖等关系,可以直接使用,不必手动梳理
    APP_PIPE
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(
        // 一些自定义的中间件,同app.use,但自定义中间件放在这里更符合开发规范
      )
  }
}

其中,依赖注入又是NestJs最核心的功能之一,一句话概况就是,依赖注入是代码解耦 (Decoupling) 的一种实现方式,通过将依赖的 class 的构造函数调用移动到调用方 class 外部的方式,来实现调用方 class 和依赖 class 解耦。因为这里面知识点比较多,且非本文重点,这里不做太多详细介绍,有兴趣的可以看一下这篇文章Nest.js 依赖注入

3️⃣ Controller
和 MVC 中 Controller 的概念一样;可以理解为是服务的入口和出口。一般来说都是薄薄的一层,主要起到了路由 (Routing) 的作用。当然,其和imports中模块内部自身的Controller是共存的;

4️⃣ 连接数据库
通过上面的一个简单介绍,大家可以了解到,一个简单的NestJs程序是什么样的流转逻辑,当然了,实际开发中,我们的模块会非常多,这时只需要在imports中引入即可,每一个模块(module)的结构也是个上面提到的一样,imports不仅可以引入其他模块,还可以引入数据库的配置,即该模块需要链接哪几张表。以实际代码为例,我们来看一下如何在NestJs对应模块中连接MongoDB数据库表

import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forFeature([
      {
        name: ManuScript.name,
        schema: ManuScriptSchema,
        collection: 't_manuscript', // 指定collection 表名称(不指定默认加s)
      },
      {
        name: PageGroup.name,
        schema: PageGroupSchema,
        collection: 't_manuscript_pagegroup',
      },
  ],
})

其中,

  • “schema”可以理解为表实例,里面描述着表结构,就像这样
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types } from 'mongoose';

export type MemoDocument = Memo & Document;

@Schema({
  timestamps: true,
})
export class Memo extends Document {
  @Prop({
    default: Types.ObjectId,
    index: true
  })
  memoId: string

  @Prop()
  designerItemId: string; // 关联组件标识id

  @Prop()
  docId: string; // 关联文稿id
}
export const MemoSchema = SchemaFactory.createForClass(Memo);

每一个@Prop下都是一个“表字段”,我们也可以将某一个字段设置为索引。

  • “ collection”代表表的“表名”,我们只需要在代码里这样创建即可,不需要提前创建表,也不需要提前设计定义字段,最终的表现是这样的

    这里我们使用的“MongoDB Compass”作为MongoDB的可视化工具。

5️⃣ 模板渲染
了解了上面的基本操作,那么你已经可以创建一个可用的NestJs应用了,对于Node来说,模板渲染能力也是其其中一个主要能力,我们利用该能力来渲染前端静态资源,这里会在入口文件映入一个中间件,如下

  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('html');
  app.engine('html', require('shtm').__express);

意思是,使用shtm模板渲染插件,来渲染html。同样的,渲染模板是也是需要一个“路由”或者“Controller”的path用来指定对应的渲染逻辑,如下

import { Controller, Get, Render } from '@nestjs/common';
  @Get('/main(/*)?') // 路由,到这个路由时,执行下面的渲染逻辑
  @Render('main') // 要渲染哪个html模板(文件名)。
  @ApiOperation({ summary: '首页路由' })
  main() {
    return {
      // 需要传到模板中的一些数据
    };
  }

Nest为我们的项目落地提供了哪些便利

✅ 完整丰富的生态

因为是内部创新型项目,好多东西都存在在未知的探索,nestjs为我们提供了强大的生态支持,为我们不断的完善项目功能提供强大的保障;如项目中需要SCF(长链接)来对接公司内部一些第三方服务,我们可以通过优秀的中间件机制轻松的完成;如项目中需要集成websocket,nest也有@nestjs/websockets这样的能力来提供支持;如项目中,需要使用mongoose,nest也有@nestjs/mongoose这样的能力来提供支持。这样为我们的项目创新以及扩展提供了强大的技术生态保障

✅ 优秀的团队代码管理

上面提到了,NestJs中模块化的运行机制,这种机制下, 对于团队开发的代码管理具有很大的优势,我们可以每个人负责一个相应的模块(module),在代码管理层面上,显得非常高效

写在后面

本文简述了NestJs在我们内部自研项目中的落地落地实践经历,并介绍了如何创建一个NestJs应用,如果看到文章的你,也有使用NodeJs搭建一个Server服务的想法,不妨试一试,NestJs吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容