nodejs midway+typeorm搭建后台方案

2023-10-29 06:50

本文主要是介绍nodejs midway+typeorm搭建后台方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 前言
  • midway
    • 初始化
    • 默认配置(config)
    • 接口编写(controller)
    • 服务(service)
    • 参数校验(dto)
    • jwt生成token
    • 测试(test)
    • swagger
  • typeorm
    • 创建实体(entity)
    • 使用实体创建数据库连接(data-source.ts)
    • 数据库使用(model)
  • midway+typeorm
  • 尾言

前言

近期学到了nodejs搭建企业级应用后台的一种解决方案,midway搭建的系统化服务端和typeorm对数据库的应用,记录一下。

midway

本文只做部分内容介绍,详见官方文档:midway。

初始化

$ npm init midway

因为是纯nodejs后台,我们选择默认的koa类型。

最后输入项目名称,一个初始化项目就诞生了。

默认配置(config)

/src/config/config.default.ts

import { MidwayConfig } from '@midwayjs/core';export default {// use for cookie sign key, should change to your own and keep securitykeys: '1661308181665_796',koa: {port: 7001,},
} as MidwayConfig;

在这里我们可以改服务启动的端口port,默认是7001。

接口编写(controller)

我把原有的模板改成了这样,无论是get还是post请求都可以参照下面的例子仿写。

在这里我们还可以对querybody的入参进行参数校验,会在后面提到。

/src/controller/api.controller.ts

import {Inject,Controller,Get,Query,Post,Body,
} from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import { UserService } from '../service/user.service';// 表示下列所有接口的前缀为 /api
@Controller('/api')
export class APIController {@Inject()ctx: Context;@Inject()userService: UserService;// 结合前缀,该接口完整为 /api/get_user@Get('/get_user')async getUser(@Query() query) {const { uid } = query;const user = await this.userService.getUser({ uid });return { success: true, message: 'OK', data: user };}// 结合前缀,该接口完整为 /api/post_user@Post('/post_user')async postUser(@Body() body) {const { uid } = body;const user = await this.userService.getUser({ uid });return { success: true, message: 'OK', data: user };}
}

服务(service)

在上面接口中我们会发现使用到了service的服务。

我来解释一下流程,

一般api接口通过get或者post请求得到用户的入参,根据用户调用的接口要触发的行为,去调用服务。

比如这里的接口是要通过传入uid得到用户的信息,我们就调用getUser服务,传入从接口得到的uid入参,通过服务来返回内容,这里的内容一般还需要通过数据库去查询

/src/service/user.service.ts

import { Provide } from '@midwayjs/decorator';
import { IUserOptions } from '../interface';@Provide()
export class UserService {async getUser(options: IUserOptions) {// 这里只是伪造的数据,实际项目需要通过数据库去进行真实的查询return {uid: options.uid,username: 'mockedName',phone: '12345678901',email: 'xxx.xxx@xxx.com',};}
}

getUser服务的需要的入参类型IUserOptions,我们可以在interface.ts文件中定义好。

/src/interface.ts

/*** @description User-Service parameters*/
export interface IUserOptions {uid: number;
}

参数校验(dto)

前面我们提到api接口的入参我们可以进行参数校验,如果我们每次都在接口中进行判空,判断长度限制等操作,过于繁琐,所以我们需要使用@midwayjs/validate

这里需要先提到/src/configuration.ts文件,可以简单理解为它是用来引入一些依赖的文件。

可以看到koa项目默认已经引入validate了,如果你的项目没有,需要自己引入一下。

在这里插入图片描述
引入了之后,我们创建一个dto文件夹,在里面编写参数校验文件。

Rule括号中就是对参数的校验,一个参数头顶对应一个Rule

这里的RuleType.number().required()表示入uid必须是一个非空的数字

常见的校验还有

RuleType.number().required();               // 数字,必填
RuleType.string().empty('')                 // 字符串非必填
RuleType.number().max(10).min(1);           // 数字,最大值和最小值
RuleType.number().greater(10).less(50);     // 数字,大于 10,小于 50RuleType.string().max(10).min(5);           // 字符串,长度最大 10,最小 5
RuleType.string().length(20);               // 字符串,长度 20
RuleType.string().pattern(/^[abc]+$/);      // 字符串,匹配正则格式RuleType.object().length(5);                // 对象,key 数量等于 5RuleType.array().items(RuleType.string());  // 数组,每个元素是字符串
RuleType.array().max(10);                   // 数组,最大长度为 10
RuleType.array().min(10);                   // 数组,最小长度为 10
RuleType.array().length(10);                // 数组,长度为 10RuleType.string().allow('')                 // 非必填字段传入空字符串

/src/dto/user.dto.ts

import { Rule, RuleType } from '@midwayjs/validate';export class GetUserDTO {@Rule(RuleType.number().required())uid: number;
}

我们编辑好校验文件之后,需要进行两步:

  1. 要在接口加入@Validate装饰器。
  2. 然后对入参body或者query进行类型绑定,这里例子绑定的是GetUserDTO
// ...
import { Validate } from '@midwayjs/validate';
import { GetUserDTO } from '../dto/user.dto';@Controller('/api')
export class APIController {@Inject()ctx: Context;@Inject()userService: UserService;@Post('/post_user')@Validate() // 开启功能需要在这里加入@Validate装饰器async postUser(@Body() body: GetUserDTO) {const { uid } = body;const user = await this.userService.getUser({ uid });return { success: true, message: 'OK', data: user };}
}

jwt生成token

jwt用于生成用户的token,进行以下几步:

默认的项目可能没有,我们需要手动添加依赖:

$ yarn add @midwayjs/jwt

/src/configuration.ts文件中导入:

在这里插入图片描述

然后在/src/config/config.default.ts下加入jwt配置

import { MidwayConfig } from '@midwayjs/core';export default {// use for cookie sign key, should change to your own and keep securitykeys: '1661225754650_9760',koa: {port: 3000,},jwt: {secret: 'mySecret', // keyexpiresIn: 60 * 60 * 24, // token储存时长},
} as MidwayConfig;

然后就可以在controller文件编写的接口中使用,使用时:

  1. 引入JwtService
  2. 添加jwt@Inject()装饰器。
  3. 调用this.jwt.sign()即可生成token。
// ...
import { JwtService } from '@midwayjs/jwt';@Controller('/api')
export class APIController {
// ...@Inject()jwt: JwtService;@Post('/user/login')@Validate()async loginUser(@Body() body: UserLoginDTO) {const user = {id: 1,username: 'admin',password: '123456',};const token = await this.jwt.sign({ ...user });return {code: 200,result: 'success',message: '登录成功',data: {token,},};}
}

测试(test)

midway的项目采用jest来进行测试。

test文件夹中新建比如user.test.ts文件。

我在这里提供一个可以复用app,可以在一个文件中执行多项test的模板。

一个it函数就是一个测试。

接口调用可以仿写,重点关注expect断言,

这里只用到了这些,所有断言详见官方文档:jest断言。

expect(x).toBe(y) // 代表测试x必须等于y
expect(x).toMatchObject(y); // 代表测试x对象包含y,也就是简单理解为y是x的子集
expect(x).toHaveProperty(y); // 代表x对象要包含y属性,y可以嵌套
expect(x).toStrictEqual(y); // 代表x要和y的对象、值结构严格相等

/test/user.test.ts

import {createApp, close, createHttpRequest} from '@midwayjs/mock';
import {Framework, Application} from '@midwayjs/koa';describe('test/controller/user.test.ts', () => {let app: Application;beforeAll(async () => {// 只创建一次 app,可以复用try {// 由于Jest在BeforeAll阶段的error会忽略,所以需要包一层catch// refs: https://github.com/facebook/jest/issues/8688app = await createApp<Framework>();} catch (err) {console.error('test beforeAll error', err);throw err;}});afterAll(async () => {// close appawait close(app);});it('正常登录测试', async () => {const result = await createHttpRequest(app).post('/api/user/login').send({username: 'jack',password: 'redballoon'});expect(result.status).toBe(200);expect(result.body).toMatchObject({code: 200,result: 'success',message: '登录成功',});expect(result.body).toHaveProperty('data.token')});it('异常登录测试', async () => {const result = await createHttpRequest(app).post('/api/user/login').send({username: 'jack',password: 'xxxxx'});expect(result.status).toBe(200);expect(result.body).toStrictEqual({code: 400,result: 'error',message: '账号或密码不正确',data: null,});});});

有时候我们还需要对接口请求时间进行严格控制,比如控制在一秒内。

我们可以在根目录下添加jest.setup.js文件,将超时时长控制在1000(单位ms)。

// jest.setup.js
jest.setTimeout(1000);

然后引入根目录下的jest.config.js文件:

// jest.config.js
module.exports = {preset: 'ts-jest',testEnvironment: 'node',testPathIgnorePatterns: ['<rootDir>/test/fixtures'],coveragePathIgnorePatterns: ['<rootDir>/test/'],setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],  // 读取 jest.setup.js
};

swagger

$ yarn add @midwayjs/swagger@3 swagger-ui-dist 

configuration.ts 中增加组件。

import { Configuration } from '@midwayjs/decorator';
import * as swagger from '@midwayjs/swagger';@Configuration({imports: [// ...swagger]
})
export class MainConfiguration {}

加入默认测试入参。

我们在参数校验内容的部分提到dto文件,用于声明入参类型,我们可以在该文件中加入入参的swagger测试入参:

  1. 引入ApiProperty
  2. 使用@ApiProperty装饰器。

/src/dto/user.dto.ts

import { Rule, RuleType } from '@midwayjs/validate';
import { ApiProperty } from '@midwayjs/swagger';export class GetUserDTO {@Rule(RuleType.number().required())@ApiProperty({example: '123456',description: '入参uid',})uid: number;
}

启动之后访问(如果修改了默认端口,7001也要同步修改):

  1. ui: http://127.0.0.1:7001/swagger-ui/index.html
  2. json: http://127.0.0.1:7001/swagger-ui/index.json

在这里插入图片描述

typeorm

用于处理数据库操作的库,我们可以在midway项目基础上引入以下包来使用。

本文只做部分内容介绍,我们这里的示例使用电脑的内存数据库sqlite,其余数据库使用方法详见文档地址:TypeORM 中文文档。

建议使用yarnnpm有时候会出现sqlite3安装失败的问题。

$ yarn add reflect-metadata typeorm sqlite3

创建实体(entity)

在创建sqlite数据库连接之前,我们先要创造一个数据实体,就是数据库要存放的数据对象类型。

我们在src下新建一个entity文件夹,然后我们可以创造一个这样的实体:

可以定义类型、含义等内容。

/src/entity/user.entity.ts

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';@Entity()
export class UserEntity {// PrimaryGeneratedColumn修饰,自动递增的主键@PrimaryGeneratedColumn({type: 'int',name: 'id',comment: '用户的自增ID',})id: number;// Column修饰,其他属性@Column('varchar', {name: 'username',comment: '用户名',length: 64,})username: string;@Column('varchar', {name: 'password',nullable: true,comment: '用户密码',length: 64,})password: string | null;
}

使用实体创建数据库连接(data-source.ts)

src下新建data-source.ts文件,将实体引入,再引入reflect-metadatatypeorm的连接数据库方法。

database这里:memory:指定的是内存数据库,如果想要创建可供观察的数据库,可以改成database.sqlite这样的,带sqlite后缀,会在项目根目录下生成sqlite文件作为数据库。

import 'reflect-metadata';
import { createConnection } from 'typeorm';
import { UserEntity } from './entity/User.entity';export const dbConnection = createConnection({type: 'sqlite',database: ':memory:',dropSchema: true,entities: [UserEntity],synchronize: true,logging: false,
});

数据库使用(model)

既然有连接了,接下来我们只要在需要使用的地方使用就好了。

我们在src下创建model文件夹,创建有一些数据库操作的model文件。

/src/model/user.model.ts

import { Repository } from 'typeorm';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { UserEntity } from '../entity/User.entity';
import { dbConnection } from '../data-source';export class UserModel {@InjectEntityModel(UserEntity)userRepo: Repository<UserEntity>;/*** 根据用户名和密码获取用户信息* @param username {String} 用户名* @param password {String} 用户密码*/async getUserByUsernameAndPassword(username, password): Promise<UserEntity> {const ds = await dbConnection;const db = await ds.getRepository(UserEntity);return await db.findOneBy({ username, password });}
}

其中使用我们如何得到可操作数据库的对象呢,就是上面的这两句

    const ds = await dbConnection;const db = await ds.getRepository(UserEntity);

接下来我们就可以用db进行一些操作:

    // 增const saveUser = new UserEntity();saveUser.username = 'jack';saveUser.password = 'redballoon';await db.save(saveUser);// 查const findUser = await db.findBy({id: 1})// 删(先查再删)const findUser = await db.findBy({id: 1})await db.remove(findUser)// 改(先查再改再存)const findUser = await db.findOneBy({id: 2})findUser.username = 'updatename'await db.save(findUser)

midway+typeorm

记得我说过,我们可以在midwayservice中进行数据库操作,所以只要在service文件中引入model中的各种数据库操作拿来使用即可。

import { Provide } from '@midwayjs/decorator';
import { IUserOptions } from '../interface';
import { UserModel } from '../model/user.model';const userModel = new UserModel();@Provide()
export class UserService {async loginUser(options: IUserOptions) {const { username, password } = options;return await userModel.getUserByUsernameAndPassword(username, password);}
}

尾言

本文只是针对完全没有midway和typeorm基础的同学,内容没有涉及很全,如果还想要深入了解,还得去官网查询,或者对我提出建议或者问题,我会适当增加文章内容。

如果本文对你有帮助的话,欢迎点赞收藏,感谢~。

这篇关于nodejs midway+typeorm搭建后台方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/298955

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

安装nodejs环境

本文介绍了如何通过nvm(NodeVersionManager)安装和管理Node.js及npm的不同版本,包括下载安装脚本、检查版本并安装特定版本的方法。 1、安装nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 2、查看nvm版本 nvm --version 3、安装

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

pico2 开发环境搭建-基于ubuntu

pico2 开发环境搭建-基于ubuntu 安装编译工具链下载sdk 和example编译example 安装编译工具链 sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 注意cmake的版本,需要在3.17 以上 下载sdk 和ex

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

如何选择SDR无线图传方案

在开源软件定义无线电(SDR)领域,有几个项目提供了无线图传的解决方案。以下是一些开源SDR无线图传方案: 1. **OpenHD**:这是一个远程高清数字图像传输的开源解决方案,它使用SDR技术来实现高清视频的无线传输。OpenHD项目提供了一个完整的工具链,包括发射器和接收器的硬件设计以及相应的软件。 2. **USRP(Universal Software Radio Periphera

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u