Express.js 与 Nest.js对比

Express.js 与 Nest.js对比

自从 Node.js 发布以来,Javascript 在后端领域的使用有所增加。由于 Node.js 的使用越来越多,每天都会有新的框架和工具发布。ExpressNest 是使用 Node.js 创建后端应用程序的最著名的框架之一,在本文中,我们将对它们进行比较。

Express

ExpressNode.js 的简约框架。尽管它涵盖了创建服务器端应用程序的几个核心方面,但由于其简单性、灵活性和性能,它很流行,甚至 Nest 也是构建在 Express 之上的。但是,express 仍然存在一些问题,在我们深入研究这些问题之前,我们需要了解它们为我们提供了什么,让我们不使用任何框架而仅使用 Node.js 创建一个 Web 服务器。

const http = require("node:http");

// Create a local server to receive data from
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ message: "Hello World!" }));
});

server.listen(8000);

在上面的代码中,我们使用 Node.js 中内置的 HTTP 模块创建一个 Web 服务器。如果我们向http://localhost:8000发送 Web 请求,将从我们的 Node.js 服务器收到一条消息 (Hello World)。

这很简单,但是如果我们想创建一个包含数百条不同路由的 REST API 该怎么办?然后我们必须编写一个路由匹配器并将每个路由发送到其特定的控制器。另外,我们必须实现 GETPOSTPUTHTTP 方法来满足 REST 标准,对吗?Express 所做的正是这个,express 为我们处理请求/响应控制、路由、提供静态文件和中间件。这就是 Express 如此轻量和简单的美妙之处。现在让我们看看如何使用express 创建之前的应用程序。

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send({ message: "Hello World" });
});

app.listen(8000);

上面的代码也做了同样的事情。但是express为我们提供了一个与请求的URL匹配的路由机制,所以如果我们想使用express router创建一个cat路由,它会看起来像这样。

const express = require("express");
const app = express();
const router = express.Router();

router.get("/", (req, res) => {
  res.send("Hello Cats");
});

router.post("/create", (req, res) => {
  res.send("Create New Cat!");
});

app.use("/cats", router);
app.listen(8000);

这很好,但是Express有什么问题呢?Express 非常简约且直接,为用户提供了灵活性。灵活性对于有经验的用户或复杂的场景非常有利,但灵活性会导致错误和结构错误的增加。此外,现在的应用程序需要大量额外的逻辑,例如请求验证、授权、文档、测试、日志记录等。因此,Express 只为我们提供了一些功能,人们需要使用其他库或框架来解决这些需求。这就是 Nest.js 存在的原因。

Nest

Nest 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。Nest 构建在常见 Node.js 框架(ExpressFastify)之上。它使用渐进式 JavaScript,使用 TypeScript 构建并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 进行编码),并结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数式反应式编程)的元素。

Nest 在这些常见 Node.js 框架(Express/Fastify)之上提供抽象,并将其 API 直接公开给开发人员。这使开发人员可以自由地使用可用于底层平台的无数第三方模块。Nest 的目的是创建高度可测试、可扩展、松散耦合且易于维护的应用程序。

为了证明这一点,让我们使用 Nest 重构之前的应用:

import { Controller, Get, Post } from '@nestjs/common';
@Controller('cat')
export class CatController {
  @Get()
  getCats() {
    return 'Hello World';
  }
  @Post('create')
  create() {
    return 'Create New Cat!';
  }
}

Nest 的路由方法与控制器配合使用,控制器可以组织路由并使路由更清晰、更易于管理。Nest 有一个默认的错误处理程序和其他内置实用程序,可帮助我们快速入门。当然,可以用express手动实现它们,但不需要重新发明轮子。所有这些便利设置使 Nest 成为初学者的更好选择,因为这些核心实用程序对于初学者来说可能很难理解。

对比

现在我们了解了这些框架的基础知识,让我们深入了解、对比一下ExpressNext

1. 架构

Express 中,没有架构标准。这在许多大型项目或微服务应用程序中成为一个问题,因为它们需要强大而灵活的架构来保持应用程序的可维护性。

另一方面,Nest 最强大的一面是架构,因为 Nest 有许多实用程序来提供灵活、干净且​​强大的架构。Nest-Modules 就是一个很好的例子。Nest-Module 用于组织应用程序结构并帮助开发人员管理模块的依赖关系。

Nest 还非常适合应用 N 层架构,旨在将应用程序划分为逻辑层。层是一种分离职责和管理依赖关系以实现关注点分离 (SoC) 原则的方法。为了实现这一目标,Nest 建议调用控制器内的服务层,并在这些服务层内执行所有业务逻辑。对于服务层,调用另一个层称为存储库层,该层负责数据访问。因此,我们将我们的关注点分为三层。分离这些层可以为软件提供可重用性和可维护性。它还使我们的应用程序易于测试,

我们创建的cat路由,它可能看起来像这样:

CatController -> CatService -> CatRepository

在这里插入图片描述
因此,每当我们需要在任何控制器中调用 CatService 的方法时,我们只需创建 CatService 的实例并调用该方法,或者我们可以通过将 CatService 实例作为 CatController 构造函数的参数传递来使用现有的 CatService 实例,即Nest还有一个概念叫做依赖注入。

2. 依赖注入

依赖注入是一种设计模式,其中一个对象接收它所依赖的其他对象。依赖注入是控制反转的一种形式,旨在分离构造对象和使用对象的关注点,从而导致松散耦合的程序。

Nest 中,可以通过在要注入的模块顶部添加 Injectable 装饰器来使用依赖注入。例如

import { Injectable } from '@nestjs/common';
@Injectable()
export class CatService {
  findAll() {
    return 'Hello World';
  }
  create() {
    return 'Create New Cat!';
  }
}
import { Controller, Get, Post } from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
  constructor(private readonly catService: CatService) {}

  @Get()
  getCats() {
    return this.catService.findAll();
  }

  @Post('create')
  create() {
    return this.catService.create();
  }
}

正如我们所看到的,我们将 catService 实例作为参数传递给 CatController 的构造函数。这种方法使应用程序更易于维护且易于测试,尤其是对于大型应用程序。这是使用 Nest 的最大优势之一。

3. 中间件

默认情况下,Nest 中间件相当于 Express 中间件。在 Express 中间件中,我们只需创建另一个在控制器之前/之后运行的路由处理程序。Nest 有一个MiddlewareConsumer类,它是一个辅助类。它提供了几种内置方法来管理中间件。所有这些都可以简单地以流畅的方式链接起来。MiddlewareConsumer 有一个forRoutes属性,我们可以在其中简单地输入字符串或控制器的路径来注册中间件。Nest 还有一个exclude属性,可以帮助我们排除路径或控制器。

Nest 还提供类似中间件的实用程序,例如管道、过滤器和拦截器。它们可以被认为是中间件,但它们更专注于不同的目的。例如,管道主要用于验证和数据转换。另一方面,中间件是在路由处理程序之前运行的处理程序,并且可以访问请求对象。过滤器与中间件相反。它们在路由处理程序之后运行并操作响应对象以进行错误处理等。最后,拦截器可以在调用路由处理程序之前和之后访问请求和响应对象。

Nest 还提供了一个名为 ExecutionContext 的实用程序,它提供有关当前执行过程的其他详细信息,例如接下来要执行的内容,而 Express 中间件则缺少此信息。

4. 自定义异常

Nest 为大多数场景提供了默认的异常类,例如 NotFoundExceptionUnAuthorizedException 等。这可以节省我们一些时间。此外,还可以像在express中一样创建自己的异常类。

5. 验证

请求验证在很多方面都是有益的。它可以防止错误发生,并向用户显示一条有意义的消息,表明他们发送了错误的输入。此外,它还可以防止用户发送不需要的输入,从而使应用程序更加安全。

express中,我曾经在处理程序之前添加一个中间件,这个中间件采用一个验证器作为参数,该验证器是使用Joi工具创建的。但这非常耗时,并且会导致我们的路由/处理程序声明看起来更长、更复杂,并且有时候会忘记在处理程序之前添加验证器。不可能创建可在整个应用程序的所有上下文中使用的通用中间件。这是因为中间件不知道执行上下文,包括将被调用的处理程序及其任何参数。

6. 授权

授权是后端应用程序中的常见需求。某些服务可能需要基于角色的授权以防止不允许的操作。授权通常由传统 Express 应用程序中的中间件处理,通常看起来像这样。

router.get('/create', authorize(Role.Admin), create);

Nest 的授权方式类似,但 Nest 通过其Guards概念提供了一些便利。守卫确定给定的请求是否将由路由处理程序处理。防护可以是控制器范围、方法范围或全局范围,这在实现中提供了易用性,也有助于保持代码干燥和声明性。此外,执行上下文可用于构建通用防护,这使得防护比传统中间件更强大。

@Post() 
@Roles('admin') 
async create(@Body() createCatDto: CreateCatDto) { 
  this.catsService.create(createCatDto); 
}

7. 数据库

express中我们通常使用 ORM(对象关系映射器)或ODM(对象文档映射器)如SequelizeMongoose 来处理数据库操作。Nest 没有什么不同,但 Nest 具有内置的 ORM-ODM 工具,这些工具提供模型/存储库注入、可测试性和动态配置,以便访问我们选择的数据库,我认为,Nest 的架构使使用数据库变得更容易

8.API文档

迄今为止,在 Nest.js 中记录 API 是最简单的。Nest 提供了一个 swagger 模块,可以自动为 API 端点创建文档。此外,还可以使用如下装饰器定义请求/响应模式。

  @ApiBody({ type: CreateCatDto })
  @ApiCreatedResponse({ type: CreateCatResponseDto })
  create(@Body() createSampleDto: CreateCatDto) : CreateCatResponseDto{
    // ...
  }

9. 性能

Nest 允许使用 Fastify 适配器,它比 Express 适配器快两倍,如果不使用 Fastify 适配器,Nest 将无法击败 Express。我们知道 Nest 提供了这两个适配器的抽象以在它们之间进行切换,但这在每种情况下都是不可能的,因为在某些情况下这两个适配器的行为本质上不同。例如,fastify 适配器不支持嵌套路由,因此应该使用 Express 适配器来实现这一点。

10. 测试

Nest 最强大的架构优势也在这里获胜。正如我们之前讨论的那样,Nest 使用模块并将它们与依赖项注入结合起来,这允许将任何依赖项注入到任何模块,因此我们可以注入测试服务而无需实例化它,并且它可以节省大量时间和精力,特别是对于较大的模块和服务。

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

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

相关文章

【入门篇】1.1 redis 基础数据类型详解和示例

文章目录 1. 简介2. Redis基础数据类型2.1 String类型场景示例常用命令示例 2.2 List类型场景示例 2.3 Set类型场景示例 2.4 Hash类型场景示例 2.5 Sorted Set类型 3. 使用Redis存储数据的注意事项1. 内存管理2. 数据持久化3. 高并发下的性能考量 4. 参考资料 1. 简介 Redis概…

app在线客服系统怎么对接

随着移动互联网的快速发展,越来越多的企业开始意识到在线客服系统的重要性。而对于一个App来说,一个高效的在线客服系统更是必不可少的。本文将介绍如何对接App在线客服系统,提高用户体验和客户满意度。 一、在线客服系统的作用和优势 1. 提供…

【Linux】:进程间通信

进程间通信 一.基本概念二.简单的通信-管道1.建立通信信道2.通信接口 一.基本概念 是什么 两个或多个进程实现数据层面的交互。 因为进程独立性的存在,导致进程间的通信成本比较高。 为什么 因为我们有多进程协同的需求。 怎么办 a.进程间通信的本质:必须让不…

TMS320F28335使用多个串口时,SCIRXST Register出现错误

TMS320F28335使用多个串口时,SCIRXST Register出现错误 void ClearErrorState(void) {if((SciaRegs.SCIRXST.bit.FE 1)||(SciaRegs.SCIRXST.bit.BRKDT 1)){SciaRegs.SCICTL1.bit.SWRESET 0;SciaRegs.SCICTL1.bit.SWRESET 1;}if((ScibRegs.SCIRXST.bit.FE 1)||(S…

反转链表(图解)

LCR 024. 反转链表 - 力扣(LeetCode) 题目描述 给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。 样例输入 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2&…

【漏洞复现】NUUO摄像头存在远程命令执行漏洞

漏洞描述 NUUO摄像头是中国台湾NUUO公司旗下的一款网络视频记录器,该设备存在远程命令执行漏洞,攻击者可利用该漏洞执行任意命令,进而获取服务器的权限。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律&…

leetcode栈和队列三剑客

用队列实现栈 队列是先进先出的,而栈是只能在栈顶进行出栈和入栈,那我们这道题要用队列来实现栈的话,这里给的思路是两个队列,因为两个队列的话就可以相互导数据,比如我们来实现这个题目的push函数,我们的栈…

根据店铺ID/店铺链接/店铺昵称获取京东店铺所有商品数据接口|京东店铺所有商品数据接口|京东API接口

要获取京东店铺的所有商品数据,您需要使用京东开放平台提供的API接口。以下是一些可能有用的API接口: 商品SKU列表接口:该接口可以获取指定店铺下的所有商品SKU列表,包括商品ID、名称、价格等信息。您可以使用该接口来获取店铺中…

springMvc中的拦截器【巩固】

先实现下想要的拦截器功能 package com.hmdp.utils;import com.hmdp.entity.User; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Ht…

python_主动调用其他类的成员

# 主动调用其他类的成员 # 方式一: class Base(object):def f1(self):print("5个功能") class Foo(object):def f1(self):print("3个功能")# Base.实例方法(自己传self),与继承无关Base.f1(self)obj Foo() obj.f1()print("#"*20)# 方式二:按照类…

jbase虚拟M层的设计

对于只是自己产品内部使用的打印程序来说(比如打印收费单,打印结算单等),打印逻辑写在js,获取其他层都是没毛病的。但是对于类型检验报告这种打印来说,打印格式控制逻辑写在js层是百分百不行的。因为检验报…

【proverif】proverif的语法-各种密码原语的介绍和具体编码

proverif-系列文章目录 【proverif】proverif的下载安装和初使用【proverif】proverif的语法-解决中间人攻击-代码详解【proverif】proverif的语法2-各种密码原语的编码 (本文) 文章目录 proverif-系列文章目录前言铺垫知识一、对称加密二、非对称加密三…

大模型幻觉成应用落地难题 最新评测文心一言解决幻觉能力最好

大模型中的幻觉问题 “林黛玉倒拔垂杨柳”、“月球上面有桂树”、“宋江字武松”……相信经常使用大语言模型都会遇到这样“一本正经胡说八道”的情况。这其实是大模型的“幻觉”问题,是大模型行业落地的核心挑战之一。例如幻觉会影响生成内容的可靠性,…

大数据基础设施搭建 - Hadoop

文章目录 一、下载安装包二、上传压缩包三、解压压缩包四、配置环境变量五、测试Hadoop5.1 测试hadoop命令5.2 测试wordcount案例5.2.1 创建wordcount输入文本信息5.2.2 执行程序5.2.3 查看结果 六、分发压缩包到集群中其他机器6.1 分发压缩包6.2 解压压缩包6.3 配置环境变量 七…

TDengine Restful Authorization 自定义Token

Restful 接口是 TDengine 最常用的接口,仅次于 JDBC。TDengine 支持 HTTP 和 HTTPS,但通常情况下,大家不想搞证书,又在内网环境中,采用 HTTP 方式比较多。但 HTTP 是明文传输,只要抓个包就知道账号密码了。…

2.4 矩阵的运算法则

矩阵是数字或 “元素” 的矩形阵列。当矩阵 A A A 有 m m m 行 n n n 列,则是一个 m n m\times n mn 的矩阵。如果矩阵的形状相同,则它们可以相加。矩阵也可以乘上任意常数 c c c。以下是 A B AB AB 和 2 A 2A 2A 的例子,它们都是 …

YOLOv5项目实战(4)— 简单三步,教你按比例划分数据集

前言:Hello大家好,我是小哥谈。本节课就教大家如何去按照比例去划分数据集,希望大家学习之后可以有所收获!~🌈 前期回顾: YOLOv5项目实战(1)— 如何去训练模型 YOLOv5项目

万字长文:从 C# 入门学会 RabbitMQ 消息队列编程

RabbitMQ 简介 RabbitMQ 是一个实现了 AMQP 协议的消息队列,AMQP 被定义为作为消息传递中间件的开放标准的应用层协议。它代表高级消息队列协议,具有消息定位、路由、队列、安全性和可靠性等特点。 目前社区上比较流行的消息队列有 kafka、ActiveMQ、Pul…

freeRTOS--软件定时器

一、什么是定时器: 简单可以理解为闹钟,到达指定一段时间后,就会响铃。STM32 芯片自带硬件定时器,精度较高、达到定时时间后会触发中断,也可以生成 PWM 、输入捕获、输出比较,等等,功能强大&am…

基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码

基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于黄金正弦优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…