
-
学习装饰器语法,感受其简洁优美; -
自己学习一门新的开发框架,感受不同框架的优缺点,为以后开发选型打基础; -
感受服务端排查问题的复杂性,找找前端设计的灵感。
-
https://www.geeksforgeeks.org/best-nodejs-frameworks-for-app-development/ -
https://anywhere.epam.com/business/best-node-js-frameworks
-
【必须】没有任何数据库,完成接口请求运行,能够跑起来; -
【必须】创建基础数据库 MySQL ,接入 @nestjs/sequelize 库 完成 增删改查 功能即:CRUD -
【可选】打算采取 Graphql 处理 API 查询,做到精确数据查询,这个已经火很久了,打算后续可以直接用到业务上。 -
【可选】接入 Swagger 自动生成 API 文档,快捷进行前端与后端服务联调测试。Swagger是一个开源工具,用于设计、构建、记录和使用RESTful web服务。 -
【可选】接口请求,数据库优化处理
-
请求分流,数据库写入加锁,处理并发流程 -
增加 middleware 中间件统一处理请求及响应,进行鉴权处理,请求拦截等操作 -
数据库分割备份,数据库容灾处理,分为:主、备、灾 -
数据库读写分离,数据双写,建立数据库缓存机制,使用 redis 处理
# 进入文件夹目录cd full-stack-demo/packages# 安装脚手架npm i -g @nestjs/cli# 创建基础项目nest new node-server-demo# 进入项目cd new node-server-demo# 运行项目测试npm run start:dev
-
common - 公用方法类 -
config - 配置类文件 -
controller - 控制器,用于处理前端发起的各类请求 -
service - 服务类,用于处理与数据库交互逻辑 -
dto - DTO(Data Transfer Object)可以用于验证输入数据、限制传输的字段或格式。 -
entities - 实体类,用于描述对象相关的属性信息 -
module - 模块,用于注册所有的服务类、控制器类,类似 Spring 里面的 bean
这里不能完全等同哈,两个实现机制上就不同,只是帮助大家理解。
main.ts - nest 启动入口
types - typescript 相关声明类型
-
控制器 controller
// packages/node-server-demo/src/controller/user/index.tsimport { Controller, Get, Query } from '@nestjs/common';import UserServices from '@/service/user';import { GetUserDto, GetUserInfoDto } from '@/dto/user';export class UserController {constructor(private readonly userService: UserServices) {}// Get 请求 user/name?name=bricechouasync findByName( getUserDto: GetUserDto) {return this.userService.read.findByName(getUserDto.name);}// Get 请求 user/info?id=123async findById( getUserInfoDto: GetUserInfoDto) {const user = await this.userService.read.findById(getUserInfoDto.id);return { gender: user.gender, job: user.job };}}
// packages/node-server-demo/src/controller/log/add.tsimport { Controller, Post, Body } from '@nestjs/common';import { AddLogDto } from '@/dto/log';import LogServices from '@/service/log';('log')export class CreateLogController {constructor(private readonly logServices: LogServices) {}// post('/log/add')('add')create(() createLogDto: AddLogDto) {return this.logServices.create.create(createLogDto);}}
-
数据转换 Data Transfer Object
// packages/node-server-demo/src/dto/user.tsexport class CreateUserDto {name: string;age: number;gender: string;job: string;}// 可以分开写,也可以合并export class GetUserDto {id?: number;name: string;}// 可以分开写,也可以合并export class GetUserInfoDto {id: number;}
-
service 数据库交互处理类
// packages/node-server-demo/src/service/user/read.tsimport { Injectable } from '@nestjs/common';import { User } from '@/entities/User';()export class ReadUserService {constructor() {}async findByName(name: string): Promise<User> {// 可以处理判空,从数据库读取/写入数据,可能会被多个 controller 进行调用console.info('ReadUserService findByName > ', name);return Promise.resolve({ id: 1, name, job: '程序员', gender: 1, age: 18 });}async findById(id: number): Promise<User> {console.info('ReadUserService findById > ', id);return Promise.resolve({id: 1,name: 'BriceChou',job: '程序员',gender: 1,age: 18,});}}
-
module 模块注册,服务类/控制类
// packages/node-server-demo/src/module/user.tsimport { Module } from '@nestjs/common';import UserService, { ReadUserService } from '@/service/user';import { UserController } from '@/controller/user';@Module({providers: [UserService, ReadUserService],controllers: [UserController],})export class UserModule {}
// packages/node-server-demo/src/module/index.ts 根模块注入import { Module } from '@nestjs/common';import { UserModule } from './user';import { LogModule } from './log';@Module({imports: [UserModule,LogModule,],})export class AppModule {}
-
main.js 启动注册的所有类
// packages/node-server-demo/src/main.tsimport { AppModule } from '@/module';import { NestFactory } from '@nestjs/core';import { NestExpressApplication } from '@nestjs/platform-express';async function bootstrap() {const app = await NestFactory.create<NestExpressApplication>(AppModule);// 监听端口 3000await app.listen(3000);}bootstrap();
-
注意:安装的数据库,一定要设置密码,连接数据库必须要有密码,否则会导致连接数据库失败。 -
MySQL 我们只安装数据库就行,熟悉指令的童鞋,就直接命令行操作就行。 -
不熟悉的话,那就下载图形化管理工具。
-
PRIMARY KEY 是表中的一个或多个列的组合,它用于唯一标识表中的每一行。 -
Not NULL 和 Unique 就不解释,就是直译的那个意思。 -
GENERATED 生成列是表中的一种特殊类型的列,它的值不是从插入语句中获取的,而是根据其他列的值通过一个表达式或函数生成的。
CREATE TABLE people (first_name VARCHAR(100),last_name VARCHAR(100),full_name VARCHAR(200) AS (CONCAT(first_name, ' ', last_name)));
-
UNSIGNED 这个数值类型就只能存储正数(包括零),不会存储负数。 -
ZEROFILL 将数值类型的字段的前面填充零,他会自动使字段变为 UNSIGNED,直到该字段达到声明的长度,如:00007 -
BINARY 用于存储二进制字符串,如声明一个字段为 BINARY(5),那么存储在这个字段中的字符串都将被处理为长度为 5 的二进制字符串。
-
此外也可顺手创建一个索引,方便快速查找。
CREATE TABLE `rrweb`.`test_sys_req_log` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,`content` TEXT NOT NULL,`l_level` INT UNSIGNED NOT NULL,`l_category` VARCHAR(255) NOT NULL,`l_created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`l_updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,INDEX `table_index` (`l_level` ASC, `l_category` ASC, `l_time` ASC) VISIBLE);3.连接数据库
-
安装 Sequelize
# 安装连接库npm install --save @nestjs/sequelize sequelize sequelize-typescript mysql2# 安装 typenpm install --save-dev @types/sequelize
-
配置数据库基础信息
// packages/node-server-demo/src/module/index.tsimport { Module } from '@nestjs/common';import { UserModule } from './user';import { LogModule } from './log';import { Log } from '@/entities/Log';import { SequelizeModule } from '@nestjs/sequelize';@Module({imports: [SequelizeModule.forRoot({dialect: 'mysql',// 按数据库实际配置host: '127.0.0.1',// 按数据库实际配置port: 3306,// 按数据库实际配置username: 'root',// 按数据库实际配置password: 'hello',// 按数据库实际配置database: 'world',synchronize: true,models: [Log],autoLoadModels: true,}),LogModule,UserModule,],})export class AppModule {}
-
实体与数据库一一映射处理
import { getNow } from '@/common/date';import {Model,Table,Column,PrimaryKey,DataType,} from 'sequelize-typescript';({ tableName: 'test_sys_req_log' })export class Log extends Model<Log> {({type: DataType.INTEGER,autoIncrement: true,field: 'id',})id: number;({ field: 'content', type: DataType.TEXT })content: string;({ field: 'l_level', type: DataType.INTEGER })level: number; // 3严重,2危险,1轻微({ field: 'l_category' })category: string; // 模块分类/来源分类({field: 'l_created_at',type: DataType.NOW,defaultValue: getNow(),})createdAt: number;({field: 'l_updated_at',type: DataType.NOW,defaultValue: getNow(),})updatedAt: number;}
-
module 注册实体
// packages/node-server-demo/src/module/log.tsimport { Module } from '@nestjs/common';import { SequelizeModule } from '@nestjs/sequelize';import { Log } from '@/entities/Log';import LogServices, {CreateLogService,UpdateLogService,DeleteLogService,ReadLogService,} from '@/service/log';import {CreateLogController,RemoveLogController,UpdateLogController,} from '@/controller/log';@Module({imports: [SequelizeModule.forFeature([Log])],providers: [LogServices,CreateLogService,UpdateLogService,DeleteLogService,ReadLogService,],controllers: [CreateLogController, RemoveLogController, UpdateLogController],})export class LogModule {}
-
service 操作数据库处理数据
import { Log } from '@/entities/Log';import { Injectable } from '@nestjs/common';import { AddLogDto } from '@/dto/log';import { InjectModel } from '@nestjs/sequelize';import { ResponseStatus } from '@/types/BaseResponse';import { getErrRes, getSucVoidRes } from '@/common/response';()export class CreateLogService {constructor((Log)private logModel: typeof Log,) {}async create(createLogDto: AddLogDto): Promise<ResponseStatus<null>> {console.info('CreateLogService create > ', createLogDto);const { level = 1, content = '', category = 'INFO' } = createLogDto || {};const str = content.trim();if (!str) {return getErrRes(500, '日志内容为空');}const item = {level,category,// Tips: 为防止外部数据进行数据注入,我们可以对内容进行 encode 处理。// content: encodeURIComponent(str),content: str,};await this.logModel.create(item);return getSucVoidRes();}}
-
C create 创建 -
R read 读取 -
U update 更新 -
D delete 删除
import { Injectable } from '@nestjs/common';import { InjectModel } from '@nestjs/sequelize';import { User } from './user.model';()export class UserService {constructor((User)private userModel: typeof User,) {}// 创建新数据async create(user: User) {const newUser = await this.userModel.create(user);return newUser;}// 查找所有数据async findAll() {return this.userModel.findAll();}// 按要求查找单个async findOne(id: string) {return this.userModel.findOne({ where: { id } });}// 按要求更新async update(id: string, user: User) {await this.userModel.update(user, { where: { id } });return this.userModel.findOne({ where: { id } });}// 按要求删除async delete(id: string) {const user = await this.userModel.findOne({ where: { id } });await user.destroy();}}

