优势:

● 代码架构合理,装饰器语法,概念比较多;

● TS原生支持,体验好,项目的代码质量高;

● 对于代码人员的要求更高,上手有难度( Egg.js/Express/koa )

适合nodejs开发场景:

● 聊天室、爬虫等高并发的应用(同时对CPU密集不敏感)

● 缺少后端但需要快速上线的项目

● Serverless及前后端一体化的项目,CLI及中间层等

![[Pasted image 20251230110953.png]]

Node.js是 一个基于 Chrome V8 引擎的 JavaScript运行环境

通俗的理解:Node.js 为 JavaScript 代码的正常运行,提供的必要的环境。

Node.js 的官网地址: Node.js — 在任何地方运行 JavaScript

承上:js可以在node环境中运行

启下:可以在跨端环境下去运行的,底层运行于各种operating system都可以

官方CLI创建模版

npm install -g @nestjs/cli

要创建、构建并运行一个全新的基础 Nest 项目(开发模式),请进入新项目的父目录,并执行以下命令:

nest new my-nest-projectcd my-nest-projectnpm run start:dev

在浏览器中打开 http://localhost:3000 即可查看运行中的新应用。当您修改任何源文件时,应用程序会自动重新编译并加载。

提示

我们推荐使用 SWC 构建器以获得更快的构建速度(性能比默认 TypeScript 编译器快 10 倍)。

当你运行 nest new 命令时,Nest 会通过创建一个新文件夹并生成初始文件集来构建一个样板应用结构。我们将其生成的项目结构称为标准模式。Nest 还支持另一种用于管理多个项目和库的结构,称为 monorepo 模式 。

除了关于构建过程如何运作的一些特殊考虑(本质上,monorepo 模式简化了有时会因 monorepo 风格项目结构而产生的构建复杂性),以及内置的库支持外,Nest 的其他功能及本文档内容均同等适用于标准模式和 monorepo 模式的项目结构。事实上,你可以在未来任何时候轻松地从标准模式切换到 monorepo 模式,因此在学习 Nest 的过程中可以放心地推迟这个决定。

![[Pasted image 20251230111012.png]]

语法:

所有 nest 命令都遵循相同格式:

nest commandOrAlias requiredArg [optionalArg] [options]

例如:

nest new my-nest-project --dry-run

这里,new 是 commandOrAlias。new 命令有一个别名 n。my-nest-project 是 requiredArg。如果命令行未提供 requiredArg,nest 会提示输入。此外,–dry-run 有一个等效的简写形式 -d。考虑到这一点,以下命令与上述命令等效:

nest n my-nest-project -d

![[Pasted image 20251230111020.png]]

通过degit创建模版

npm i -g degit

然后使用degit来下载仓库: degit https://github.com/nestjs/typescript-starter.git

# 将模版下载到本地 nestjs-degit-demo文件夹中degit https://github.com/pezzetti/base-app-nestjs.git nestjs-degit-demo

资源/模版来源:

官方示例: nest/sample at master · nestjs/nest · GitHub

Awesome: GitHub - nestjs/awesome-nestjs: A curated list of awesome things related to NestJS 😎

nestjs中文网: https://docs.nestjs.cn/9/introduction

标准模式和Monorepo模式:

● 标准模式 :适用于构建专注于单个项目的应用程序,这些应用程序拥有自己的依赖项和设置,不需要优化模块共享或复杂构建。这是默认模式。

● monorepo 模式 :该模式将代码产物视为轻量级 monorepo 的一部分,可能更适合开发团队和/或多项目环境。它自动化了部分构建过程,便于创建和组合模块化组件,促进代码重用,简化集成测试,便于共享项目范围内的产物(如 eslint 规则和其他配置策略),且比 Git 子模块等替代方案更易使用。Monorepo 模式采用工作区的概念(在 nest-cli.json 文件中表示)来协调 monorepo 各组件间的关系。

需要注意的是,Nest 的几乎所有功能都与代码组织模式无关。这种选择唯一的影响在于项目的构成方式及构建产物的生成方式。从 CLI 到核心模块再到附加模块,所有其他功能在两种模式下工作方式完全相同。

此外,可以随时轻松地从标准模式切换到 monorepo 模式 ,因此可以暂缓做出决定,直到其中一种方法的优势更加明显。

直接执行new命令构建标准模式:

nest new my-project

目录结构:
![[Pasted image 20251230111036.png]]

在标准模式的基础上将其转换为monorepo 模式结构

● 项目是一个完整的应用程序 (通过命令 nest generate app 添加到工作区)或一个库 (通过命令 nest generate library 添加到工作区)。

cd my-projectnest generate app my-app

![[Pasted image 20251230111111.png]]

ResuflAPI设计

RESTful API需要设计序言、全局(错误码、请求BaseUrl、Proxy等)参数、修改记录以及按照功能划分的接口描述。

ResufulAPI就是Chrome中的那些请求,包含请求头各种信息组成的一个请求,符合ResufulAPI规范

一般的设计如下:
在这里插入图片描述

下面来介绍一份标准的接口设计中,重要的组成部分:

● 接口描述;

● 请求URL;

● 请求方式: POST/GET/DELETE/PUT;

● 参数: Body或者Params或者Headers参数(JWT Token)及参数说明;

● 返回示例;

● 返回参数说明;

![[Pasted image 20251230111156.png]]

MIT许可证:你的义务是“保留原作者的许可声明”。至于你的新作品用什么许可证,它不管。

GPL许可证:你的义务是“保留原作者的许可声明”,并且你的整个新作品必须“以GPL许可证发布”。

你使用了一个GPL许可的库 gpl-lib来构建你的软件 MyApp。

● 如果你不分发 MyApp(比如只作为内部工具或SaaS服务),GPL的传染性可能不触发(但AGPL会)。

● 但如果你将 MyApp作为软件分发给别人(比如卖安装包),那么对不起,整个 MyApp都必须遵循GPL协议。你必须将 MyApp的源代码也提供给你的用户。

所以,您说的“必须说明底层是被GPL所许可”是对的,但这只是第一步。更关键的是,它要求你的“整个作品”也必须在GPL之下。

使用GPL的:

![[Pasted image 20251230111206.png]]

**![[Pasted image 20251230111211.png]]**

使用MIT的:

● Node.js使用的许可证是 MIT License,这是一种非常宽松的许可证。

● npm(Node.js的包管理器)上的绝大多数包也都是MIT等宽松许可证。

● 这就是为什么公司可以毫无顾忌地使用Node.js来构建商业闭源应用。如果Node.js是GPL,那整个互联网的格局都会大不一样。

vscode插件:Choose A License,可以创建常用的开源项目所需要的license

vscode插件:licenser,单个文件/所有文件快速插入你自己的个人作者信息

![[Pasted image 20251230111217.png]]

nestjs约定大于配置:

完整设计流程:

安装cli

● 全局安装nestjs脚手架

npm i -g @nestjs/cli    

● 创建一个nestjs项目

nest new nestjs-demo

● 查看帮助文档

nest --help

● 查看g命令具体的帮助文档

nest g --help

● 启动项目

npm run start:dev 

● 创建user模块

nest g module user

注意:这一步需要删除user模块自带的控制器等等,最终只保留user.module.ts(因为其他的要重新创建)

● -d 参数,预执行(演示模式)

nest g controller user --no-spec -d

● 创建控制器:

○ 用于定义跳转路由

nest g controller user --no-spe

● 创建服务

nest g service user --no-spec
安装nestjs配置:
npm i --save @nestjs/config
配置conig模块(环境变量注入)
ConfigModule 的作用:
  1. 环境变量管理

● 自动加载 .env 文件中的环境变量

● 支持多环境配置(如 .env.development , .env.production )

● 将环境变量注入到 process.env 中

  1. 配置验证和类型安全

● 可以验证环境变量的格式和必需性

● 提供类型安全的配置访问

  1. 全局配置访问

● 通过 ConfigService 在整个应用中访问配置

● 支持嵌套配置对象

通过forRoot()初始化配置动态模块

内置了dotenv,所以可以直接对环境变量进行配置

● 下面示例中,相同模块下的导入imports能够被同模块下的controllers和providers去使用

○ 不同模块,跨模块不能够直接去进行引用
在这里插入图片描述

错误用法:

跨模块了,这里不能够使用,除非当前模块也导入

![[Pasted image 20251230111226.png]]

解释:ConfigService需要从另一个模块导出,从当前模块导入才能跨模块使用

错误:Nest 无法解析 UserController 的依赖项(UserService,?)。请确保参数 ConfigService 在索引 [1] 处在 UserModule 上下文中可用。可能的解决方案: - 如果 ConfigService 是一个提供者,它是否是当前 UserModule 的一部分? - 如果 ConfigService 从单独的 @Module 导出,该模块是否在 UserModule 中导入? @Module({imports: [/* 包含 ConfigService 的模块 */ ]})
设置ConfigModule模块在全局使用

设置isGlobal属性,就可以让当前模块在全局使用

![[Pasted image 20251230111235.png]]

使用枚举设置:

使用enum枚举环境变量,这样当环境变量的名称改变时,代码中的引用也是同步的,不会出现用string字符串引用导致找不到该值的情况

![[Pasted image 20251230111241.png]]

使用get方法调用获取环境变量

![[Pasted image 20251230111247.png]]

环境变量配置
1. dotenv环境变量配置(解析.env文件):

● 安装dotenv

npm i dotenv

● 在app.modules.ts中将ConfigModule全局引入

● load多文件配合使用

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { UserModule } from './user/user.module';
import * as dotenv from 'dotenv';

const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath,
      load: [() => dotenv.config({ path: '.env' })],
    }),
    UserModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

● 启动命令中注入环境变量:

"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main"
load配置项:

通过load配置项可以实现环境变量的配合使用

.env作为公共有的全局变量

.env.development去覆盖

.env.production去覆盖

2. yaml环境变量配置(解析yml文件)

● 安装yaml解析依赖

npm i js-yaml

● 安装js-yaml类型声明

npm i -D @types/js-yaml

● 创建yml配置文件

// config/config.ymldb:    mysql1:        host: 127.0.0.1        name: mysql-dev        port: 3306    mysql2:        host: 127.0.0.1        name: mysql-dev1        port: 3306

● 创建读取环境变量文件

![[Pasted image 20251230111254.png]]

// 导入Node.js文件系统模块的readFileSync方法,用于同步读取文件内容
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';

// 导入Node.js路径模块的join方法,用于安全地拼接文件路径
import { join } from 'path';

// 定义常量:YAML配置文件的名称
// 这里使用config.yml作为配置文件,通常包含数据库连接、服务器端口等配置信息
const YAML_CONFIG_FILENAME = 'config.yml';

// 构建完整的配置文件路径:
// __dirname: Node.js全局变量,表示当前文件所在的目录路径
// '../config': 向上级目录移动一层
// 最终路径示例:/project-root/config.yml
const filePath = join(__dirname, '../config', YAML_CONFIG_FILENAME);

/**
 * NestJS配置加载函数
 * 这是一个工厂函数,用于在应用初始化时加载YAML配置文件
 * 使用场景:在app.module.ts中通过ConfigModule.forRoot()调用
 * @returns {Record<string, any>} 返回解析后的配置对象,包含所有配置项
 */
export default () => {
  // 1. readFileSync(filePath, 'utf8'): 同步读取YAML配置文件内容
  //    - filePath: 配置文件完整路径
  //    - 'utf8': 指定文件编码格式,确保正确读取中文等特殊字符
  //    
  // 2. yaml.load(): 将YAML字符串解析为JavaScript对象
  //    - 自动处理YAML的缩进、列表、嵌套对象等语法
  //    - 返回可以直接在代码中使用的配置对象
  //    
  // 3. 返回值: 解析后的配置对象,可以在NestJS应用中通过ConfigService注入使用
  return yaml.load(readFileSync(filePath, 'utf8'));
};

在全局使用load方法注入:

![[Pasted image 20251230111308.png]]

多文件使用:
![[Pasted image 20251230111314.png]]

3. Config环境变量配置(解析JSON)

● 配置config

npm install configmkdir configvi config/default.json

● 配置环境文件

vi config/production.json

这里default会自动和其他的配置合并

![[Pasted image 20251230111319.png]]

注入到环境变量中:

注意:这里的意思是启动对应的环境可以读取该环境下的config定义的变量。就不需要单个文件引入config模块,然后用config.get获取了

● 需要安装cross-env

npm i cross-env -D

● 需要配置启动命令

"start:prod": "cross-env NODE_ENV=production| node dist/ main",
4. 其他比较好的环境变量配置

nestjsx/nestjs-config

对配置文件去做校验,而且需要有属性的提示

特定场景,支持微服务场景,支持正则表达式读取文件,支持单个模块进行局部配置变量

JOI校验

joi.dev - 17.13.3 API Reference

使用JOI库,对注入的环境变量做校验,防止使用的时候格式错误导致程序崩溃

npm install --save joi 

从源码部分得知:

![[Pasted image 20251230111326.png]]

对环境做校验

对接口添加默认值

还可以对接口添加校验范围
![[Pasted image 20251230111331.png]]

数据库连接(typeorm):

安装Mapping层相关依赖

● 安装mysql2

npm i --save @nestjs/typeorm typeorm mysql2

● 配置依赖
![[Pasted image 20251230111336.png]]

对配置做一些优化:

![[Pasted image 20251230111342.png]]

er图创建数据库:

● 方块:实体类

● 菱形:相联实体之间的关系

● 短线之间连接的数字:代表对应关系

如何创建:navicat的module,创建这个物理module就可以直接生成对应的SQL语句

小贴示:

如果去要两个表强一致,要么把他俩打成一个宽表,要么就删除插入时候都把两个表连接起来去一起增删,两种方法都可以,这种场景不算多,尽量少使用外键

数据库编程范式:

● 需求分析----逻辑设计----数据库创建----维护与优化

范式一:
![[Pasted image 20251230111349.png]]

范式二:
![[Pasted image 20251230111356.png]]

注意,如果依赖拆分过多,会导致有很多表,在查询的时候也会出现性能问题

范式三:
![[Pasted image 20251230111402.png]]

users和roles之间就是多对多多关系:

![[Pasted image 20251230111407.png]]

用中间表隔开

● 一个用户可以拥有多个角色

● 一个角色可以被多个用户拥有

如果没有中间表,只能实现一对多关系:

● 一个用户只能有一个角色(不合理)

● 或者一个角色只能属于一个用户(更不合理)

中间表的作用:

● 存储用户和角色的所有可能组合

● 灵活分配和撤销权限

● 支持复杂的权限管理

使用typeorm去创建数据库:

当连接并创建好了数据库时,此时就会自动创建下面的表

● 声明表结构:
![[Pasted image 20251230111413.png]]

● 注入:
![[Pasted image 20251230111418.png]]

在typeorm中存在很多注解帮我们去建表:
一对一

● 创建表,还有表与表之间的默认关系自动生成
![[Pasted image 20251230111424.png]]

● 以上只演示了一对一的数据库表的创建,除此之外还可以通过不同的注解创建 一对多,多对多的关系,具体看官方文档

多表查询与数据聚合(QueryBuilder)

![[Pasted image 20251230111439.png]]

● 求查询出来的count总数

● 将某些列进行求和

示例:

● .where防止sql注入
![[Pasted image 20260106203937.png]]

也可以原生query写SQL查询,这里不赘述

通过已存在的数据库逆向推理出数据库模型:

● 适用于同步老项目的数据库表结构

从已有数据库中去生成typeorm模型

npm i -g typeorm-model-generator

此处省略一万字。。。

依赖注入底层实现原理

● 将需要去复用的实体类实例化之后方便在用的时候直接调用,具体流程如下所示:

○ DI容器将所有有注解的类都标记进行注册

■ 注意这些带注解的类中可能还有别的注解,也就是嵌套了其他要注解的类,这一步就是依赖关系的理解(通过Constructor了解类与类之间的依赖关系)

■ 然后自动创建注解类的实例,以及其有依赖关系的类的实例

■ 最后就是按需调用

简单来说就是实力化带有注解的类,分析整体依赖关系,不会出现重复对类的实例化
![[Pasted image 20251230111445.png]]

将依赖的依赖进行导入,才可以在当前的”全局“去使用(依赖的依赖在当前模块注入)
![[Pasted image 20251230111449.png]]

TypeORM实现原理:

总结,就是像总线一样,将所有依赖注入到全局中,在使用的时候调用对应实例的对应方法

当在需要去用的时候发送一个事件,总线上的方法去响应事件(注意:这里要说明,跨模块的话,仔细想想当前模块的实例是出不去别的模块的,自然也就在别的模块调用不了,这里是相对的 “全局”)
![[Pasted image 20251230111455.png]]

使用TypeOrm进行crud
在service服务层去定义相关操作

● 使用 TypeORM 进行数据库操作,通过依赖注入的方式注入了 User 实体的 Repository。

// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User) private readonly userRepository: Repository<User>,
    ) { }

    // 查询所有用户
    findAll() {
        return this.userRepository.find();
    }

    // 根据用户名查找单个用户
    find(username: string) {
        return this.userRepository.findOne({ where: { username } });
    }

    // 创建新用户
    async create(user: User) {
        const userTmp = await this.userRepository.create(user);
        return this.userRepository.save(userTmp);
    }

    // 更新用户信息
    async update(id: number, user: Partial<User>) {
        return this.userRepository.update(id, user);
    }

    // 删除用户
    remove(id: number) {
        return this.userRepository.delete(id);
    }
}
在controller中去声明对应接口
// user.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { ConfigService } from '@nestjs/config';

@Controller('user')
export class UserController {
    constructor(
        private userService: UserService,
        private configService: ConfigService,
    ) { }

    @Get()
    getUsers(): any {
        return this.userService.findAll();
        // return this.userService.getUsers();
    }

    @Post()
    addUser(): any {
        const user = { username: 'toimc', password: '123456' } as User;
        // return this.userService.addUser();
        return this.userService.create(user);
    }
}

使用postman请求接口调试:

nestjs没有自带热重载,不太好,因为前端代码频繁编辑,如果有热重载不断加载反而不是很好

使用vscode自带的debug进行调试:

先选择调试器
![[Pasted image 20251230111501.png]]

根据不同的项目对调试脚本进行修改配置

● 配置文件可以指定调试模式,调试控制台…
![[Pasted image 20251230111506.png]]

调试方法:

注意:在代码中需要调试的地方打断点,在浏览器中去运行程序发送请求到后端这边,就能够成功接收请求

// 修改npm执行脚本为nestjs框架自带的 start:debug
{
    "runtimeArgs": [
        "run-script",
        "start:debug"
    ],
    "runtimeExecutable": "npm", // nodeb版本
    "runtimeVersion": "v16.16.0", // 不要去启动内置的console,而是使用我们自己的终端工具
    "internalConsoleOptions": "neverOpen"
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐