从零实现一套低代码(保姆级教程)【后端服务】 --- 【16】初始化后端项目

摘要

在前面的实现过程中,我们的低代码平台,在前端已经有一定的构建页面的能力了。

但是对于我们实现一个平台,肯定要支持用户对页面进行保存等功能,包括后面我们运行时的设计,都要依赖于后端的能力

在这里插入图片描述
所以,现在我们需要考虑开始使用数据存储了。那因为博主平时的工作主要都是前端开发,所以后端框架选择了比较贴合前端的Nest.js

我们低代码来讲,通常是对协议的保存以及修改,所以我们的数据库使用MongoDB,当然,如果读者有一定的后端开发经验,可以自行选择后端框架和数据库。

当然,选择这一些列文章看的主要可能是前端开发,所以实现后端内容的这一部分我会写的比较详细。(虽然博主的后端知识也比较匮乏,如果有问题欢迎指正)。

1.初始化Nest项目

我们来到Nest.js的中文文档:
https://nestjs.bootcss.com/controllers.html

当然,这里面很多也没有翻译,读者有兴趣可以看看,主要还是跟着我的节奏来吧。。。
首先是安装nest和创建项目。

$ npm i -g @nestjs/cli
$ nest new XinBuilderServer2

创建好之后,我们来到对应的目录下面,启动

npm run start

就可以在页面里看到Hello Word了,默认的端口号是3000, 如果想要修改的话可以在main.ts中进行修改。

现在我们来思考一下,我们要这个后端服务第一步需要干什么。对于协议,我希望我能将它保存在数据库里。

所以我们需要一个接口,用来对协议的保存和更新。

每当你选择新建一个功能模块的时候,比如现在我想有一个和存储协议相关的接口功能。
需要再控制台中输入以下命令:

nest g mo -p src pageJson  
nest g co -p src pageJson  
nest g service -p src pageJson

这三行命令是什么意思呢?就是说,在Nest中,新建一个Module + Controller + Service。这个时候你就会发现你的目录下新增了几个文件:

当然这几个名词,你也可以先不用知道什么意思(博主也知识大概理解)。后面会告诉你怎么去写。

在这里插入图片描述
你也可以手动添加。只不过费事一点而已。

2.引入MongoDB

有了后端服务之后,我们就要安装数据库了,数据库的话我们就选择MongoDB,读者可以到官网下载自己需要的安装包。

最好还是找一个教程,这里安装数据库的过程就不细写了,当你安装完后启动了,再回到项目里面。

我们需要一个库去操作数据库,在这里我们使用mongoose来操作数据库。安装的话只需要:

npm i mongoose
npm i @nestjs/mongoose

一切准备就绪之后,我们来到项目中:
xin-builder-server2\src\app.controller.ts

我们在入口的app中,引入MongoDB:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PageJsonModule } from './page-json/page-json.module';
import { MongooseModule } from '@nestjs/mongoose';

const DBRootModule = MongooseModule.forRoot('mongodb://127.0.0.1/localData');

@Module({
  imports: [DBRootModule, PageJsonModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}


切记切记,虽然上面只有短短的几句话,但是这一个过程还是比较让人烦躁的。因为在安装过程中,不确定会出现什么问题。

总之,我们的目的就是能在我们的项目中,可以和数据库建立联系。

3.实现获取页面列表的接口

对于一个模型来说,接口的工作就是增删改查,我们先写一个获取页面列表的接口。

那对于一个页面来讲,我们暂且只需要一个pageId和pageJson,分别代表页面的唯一表示和页面的JSON结构。
页面的展示还需要一个pageName作为页面名称。

在pageJson目录下新增一个page-json.interface.ts文件和pageJson.schema.ts文件,用来定义页面的结构:

xin-builder-server2\src\page-json\pageJson.schema.ts

import { Schema } from 'mongoose';
 
export const pageJsonSchema = new Schema({
  pageId: { type: String, required: true },
  pageName: { type: String, required: true },
  pageJson: { type: Object, required: true }
});

xin-builder-server2\src\page-json\page-json.interface.ts

import { Document } from 'mongoose';
 
export interface PageJson extends Document {
  readonly pageId: string;
  readonly pageJson: Object;
}

接下来我们到service中:
xin-builder-server2\src\page-json\page-json.service.ts
一个正常的service应该这么写:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { PageJson } from './page-json.interface';

@Injectable()
export class PageJsonService {
  constructor(@InjectModel('PageJson') private readonly pageJsonModel: Model<PageJson>) {}
}

有了基础准备之后,我们开始实现获取页面列表的接口:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { PageJson } from './page-json.interface';

@Injectable()
export class PageJsonService {
  constructor(@InjectModel('PageJson') private readonly pageJsonModel: Model<PageJson>) {}

  async findAllPage(): Promise<PageJson []>{
    return await this.pageJsonModel.find({})
  }
}

pageJsonModel.find()的参数就是对应的查询条件,也就是sql语句的封装。如果我传一个空对象,那么就是全量查询。

现在我们来到page-json.controller.ts中:
xin-builder-server2\src\page-json\page-json.controller.ts

import { Controller, Post } from '@nestjs/common';
import { PageJson } from './page-json.interface';
import { PageJsonService } from './page-json.service'

interface PageJsonResponse<T = unknown> {
  code: number;
  data?: T;
  message: string;
}

@Controller('page-json')
export class PageJsonController {
  constructor(private readonly PageService: PageJsonService) {}

  @Post('findAllPage')
  async findAllPage(): Promise<PageJsonResponse<PageJson[]>> {
    return {
      code: 200,
      data: await this.PageService.findAllPage(),
      message: 'Success.',
    };
  }
}

在这里我们就相当于通过调用service中的方法,去实现一个接口控制器。

最后我们修改一下page-json.module文件:

import { Module } from '@nestjs/common';
import { PageJsonController } from './page-json.controller';
import { PageJsonService } from './page-json.service';
import { MongooseModule } from '@nestjs/mongoose';
import { pageJsonSchema } from './pageJson.schema'

const pageJsonSchemaTable = MongooseModule.forFeature([{ name: 'PageJson', schema: pageJsonSchema }]);

@Module({
  imports: [pageJsonSchemaTable],
  controllers: [PageJsonController],
  providers: [PageJsonService]
})
export class PageJsonModule {}

OK,现在我们需要一个方式去验证一下了:

4.实现swagger接口文档

一般我们调试接口,都是使用postMan之类的可视化工具,在Nest中,可以直接使用swagger接口文档,我们继续安装:

ynpm i @nestjs/swagger

安装完成后我们在main.ts中将其引入:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const options = new DocumentBuilder()
  .setTitle('API example')
  .build()
  const document = SwaggerModule.createDocument(app, options)
  SwaggerModule.setup('api', app, document)
  await app.listen(4000);
}
bootstrap();

安装好之后,我们修改一下page-json.controller里的接口:

ApiTags就是这个模块的描述,ApiOperation就是这个接口的描述

import { Controller, Post } from '@nestjs/common';
import { PageJson } from './page-json.interface';
import { PageJsonService } from './page-json.service'
import { ApiTags, ApiOperation } from '@nestjs/swagger'

interface PageJsonResponse<T = unknown> {
  code: number;
  data?: T;
  message: string;
}

@ApiTags('页面管理')
@Controller('page-json')
export class PageJsonController {
  constructor(private readonly PageService: PageJsonService) {}

  @Post('findAllPage')
  @ApiOperation({summary: '查询页面列表'})
  async findAllPage(): Promise<PageJsonResponse<PageJson[]>> {
    return {
      code: 200,
      data: await this.PageService.findAllPage(),
      message: 'Success.',
    };
  }
}

这时候,我们可以通过localhost:3000/api打开对应的接口文档页面:

在这里插入图片描述

当你点击Exute之后,应该是一个空数组,因为你的数据库中并没有数据。

博主补充

OK,这一篇文章就不继续往下实现了,主要就是能将整体的安装过程以及配置过程串通。
只有这一步搞定了,我们才可以做后面的事情。

相关的代码提交在github上:
https://github.com/TeacherXin/XinBuilderServer2
commit: 初始化后端服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/323732.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

运维必存的20个常见的故障排查、修复大全

你们好&#xff0c;我的网工朋友。 “稳定是偶然&#xff0c;异常才是常态”。这句话用来形容运维的工作再合适不过了 对于运维来说&#xff0c;工作最常遇到的就是不稳定性带来的各种故障&#xff0c;经常围绕发现故障、响应故障、定位故障、恢复故障这四大步骤打转。 为此…

一篇文章带你搞懂多线程面试相关的一些问题

目录 1.Callable接口 1.1使用Callable接口来创建线程 1.1相关面试题&#xff1a; 介绍下 Callable 是什么 2.JUC常见的类&#xff08;java.util,concurrent) 2.1ReentrantLock ReentrantLock和sychronized的区别 3.信号量 4.CountDownLatch 5.线程安全的集合类 5.1多线…

Flink-容错机制

Flink中的容错机制 流式数据连续不断地到来&#xff0c;无休无止&#xff1b;所以流处理程序也是持续运行的&#xff0c;并没有一个明确的结束退出时间。机器运行程序&#xff0c;996 起来当然比人要容易得多&#xff0c;不过希望“永远运行”也是不切实际的。因为各种硬件软件…

ioDraw在线图表工具 - 轻松制作专业图表,只需3步!

还在花大量时间手动画图表&#xff1f;还在为图表样式而烦恼&#xff1f;ioDraw为你提供一站式解决方案&#xff01;ioDraw在线图表工具实现了AI自动生成图表&#xff0c;让你轻松制作专业图表&#xff0c;只需3步&#xff01; 1. 录入数据 只需将你的数据告诉ioDraw AI助手&…

alibaba.item_get API:电商行业中的数据驱动决策支持

alibaba.item_get API 是阿里巴巴提供的一个用于获取商品详情的接口。在电商行业中&#xff0c;数据驱动的决策支持是非常重要的&#xff0c;而这个 API 可以帮助你获取到商品的各种详细信息&#xff0c;从而为你的决策提供支持。 具体来说&#xff0c;通过使用 alibaba.item_…

【Oracle】期末复习题

目录 一. 单选题&#xff08;共164 题&#xff09; 二. 多选题&#xff08;共14 题&#xff09; 三. 填空题&#xff08;共4 题&#xff09; 四. 分析题&#xff08;共五题&#xff09; 一&#xff09;考生子系统 三&#xff09;考试存储方案 四&#xff09;铁路12306 …

条款24:若所有参数皆需类型转换,请为此采用非成员函数

设计一个表示有理数的类时&#xff0c;允许从整数隐式转换为有理数是有用的&#xff1a; class Rational { public:Rational(int numerator 0, // 该构造函数没有explicit限制;int denominator 1); int numerator() const; int denominator() const; const Rational opera…

CAS的超~详细介绍

什么是CAS CAS全称Compare and swap,是一种比较特殊的CPU指令. 字面意思:"比较并交换", 一个CAS涉及到以下操作: 我们假设内存中的原数据为V,旧的预期值A,需要修改的新值B. 1.比较A和V是否相等(比较) 2.如果相等,将B写入V.(交换) 3.返回操作是否成功. 伪代码 下面…

基于位的权限系统

基于位的权限系统是一种利用二进制位运算进行权限管理的技术。在这种系统中&#xff0c;不同的权限被编码为2的幂次方 (例如1、2、4、8等)&#xff0c;每个权限对应一个独立的二进制位&#xff08;可想而知运算速度是非常快的&#xff09;。通过将这些权限值组合在一起形成一个…

day13 滑动窗口最大值 前K个高频元素

题目1&#xff1a;239 滑动窗口最大值 题目链接&#xff1a;239 滑动窗口最大值 题意 长度为K的滑动窗口从整数数组的最左侧移动到最右侧&#xff0c;每次只移动1位&#xff0c;求滑动窗口中的最大值 不能使用优先级队列&#xff0c;如果使用大顶堆&#xff0c;最终要pop的…

React Native集成到现有原生应用

本篇文章以MacOS环境开发iOS平台为例&#xff0c;记录一下在原生APP基础上集成React Native React Native中文网 详细介绍了搭建环境和集成RN的步骤。 环境搭建 必须安装的依赖有&#xff1a;Node、Watchman、Xcode 和 CocoaPods。 安装Homebrew Homebrew是一款Mac OS平台下…

Java SE入门及基础(15)

Java 中的标号&#xff08;标签 label&#xff09; 1. 语法规则 标号名称 : 循环结构 2. 作用 标号的作用就是给代码添加一个标记&#xff0c;方便后面使用。通常应用在循环结构中&#xff0c;与break 语句配合使用 3. 应用场景 有如下菜单&#xff1a; 实现其中返回主菜…

C++ | 四、指针、链表

指针 指针用来储存地址定义方式&#xff0c;int *ptr;&#xff0c;使用*来表示所定义的变量是指针取地址符&#xff0c;ptr &a;&#xff0c;通过&来取得一个普通变量的地址&#xff0c;并储存到指针中取值&#xff08;解引用&#xff09;&#xff0c;想要取得一个指针…

qt.qpa.plugin: Could not find the Qt platform plugin “windows“ in ““

系统环境&#xff1a;Win10家庭中文版 Qt : 5.12.9 链接了一些64位的第三方库&#xff0c;程序编译完运行后出现 qt.qpa.plugin: Could not find the Qt platform plugin "windows" in "" 弹窗如下&#xff1a; 网上搜了一些都是关于pyQt的&#xff0c…

C++刷题 -- 栈和队列

C刷题 – 栈和队列 文章目录 C刷题 -- 栈和队列1.用栈实现队列2.用队列实现栈3.有效的括号4.前 K 个高频元素 1.用栈实现队列 力扣链接 一个栈自然实现不了队列功能&#xff0c;需要使用两个栈一个输入栈&#xff0c;一个输出栈队列是先入先出&#xff0c;当队列push操作&…

外包干了4年,废了···

有一说一&#xff0c;外包没有给很高的薪资&#xff0c;是真不能干呀&#xff01; 先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0…

专业、高效的解决方案:为企业业务提供强大支持

随着市场竞争的日益激烈&#xff0c;企业要想在众多竞争对手中脱颖而出&#xff0c;必须具备强大的核心竞争力。而这种竞争力的源泉&#xff0c;往往来自于企业所提供的产品和服务。为了确保产品和服务的质量&#xff0c;企业需要不断地进行技术创新和管理创新&#xff0c;以满…

pytest pytest-cov生成代码覆盖率报告

pytest-cov 是一个用于 pytest 的插件&#xff0c;它可以生成代码覆盖率报告。代码覆盖率是一个度量&#xff0c;表示在测试过程中执行了代码的哪些部分。这是一个非常有用的工具&#xff0c;因为它可以帮助你理解你的测试是否全面&#xff0c;是否有遗漏的代码部分。 pytest-c…

性能测试jmeter

选的这些怎么添加 在一个列表里面 方法调用${__time(YMD)} 两个下划线&#xff0c;后跟函数名&#xff0c;小括号内是输入参数&#xff0c;整个用大括号包裹。 注意POST一定要在消息体数据里面写,不能再参数里面 否则报错:loginOut,没cookie等

express服务连接mysql数据库

下载mysql2依赖包 npm i mysql2 创建mysql连接实例并暴露出去 const mysql require(mysql2)const mysqlMode mysql.createPool({host: 127.0.0.1, //服务端hostuser: root, //用户名称,mysql一般默认rootpassword: 123456, //密码database: sqlTest1, //数据库名字…