2024年Node.js框架大比拼——Elysia、Hono、Nest、Encore,选哪个最适合自己?
Node.js web框架——我们甚至都不知道从哪里开始好了。面对这么多的选择,为项目挑选合适的框架可能会让人感到无从下手。
在这篇文章中,我将带你了解 最火 的 Node.js 生态系统中的框架,分析每个框架的优点、缺点及最佳应用场景。
无论你是追求速度、可扩展性还是简洁,我们希望能涵盖所有这些方面——所以到最后,你就能准确地找到哪个框架最适合你。
我们要看看的框架有:Nest,Elysia,Encore.ts 和 Hono。
视频版:
功能简介我认为,从最轻量级到功能最丰富的,我将这些框架排列如下:
这并不意味着轻量级就是不好的,这仅仅取决于你的项目需要什么。Hono 的轻量级特性实际上是一个亮点,不到14KB,特别适合用于 Cloudflare Workers。
Encore.ts 相比之下则自带许多内置功能,比如开箱即用的自动跟踪和本地基础设施。
我们先来看看每个框架,我们先从Encore.ts开始。
Encore.ts
一个开源框架,旨在让用TypeScript构建健壮且类型安全的后端更简单。该框架自带大量工具,使开发体验更顺滑,性能上是这轮比较中最快的框架之一。
类型安全的API模式定义 | CORS处理 | 结构化日志 | 认证 |
---|---|---|---|
发布/订阅集成 | 密钥管理 | 基础设施集成 | 数据库集成 |
架构图 | 本地开发仪表板 | 服务目录 | 原生TypeScript |
调试 | 错误处理 | 事件循环 | 请求验证 |
跟踪 | API客户端生成 | 自动本地基础设施 | 自动化测试 |
Encore内置了请求验证功能。你用常规TypeScript定义的请求和响应类型将用于编译时和运行时的请求验证。与其他框架不同的是,实际的验证是由Rust而不是JavaScript来完成的。这一特点使得验证非常快,关于这一点,我们稍后会详细说明。
import {api, Header, Query} from "encore.dev/api";
enum 枚举值 {
FOO = "foo",
BAR = "bar",
}
// Encore.ts 会自动验证请求模式,如果请求不符合模式,则返回错误信息。
interface 请求验证模式 {
foo: Header<"x-foo">;
name?: Query<string>;
someKey?: string;
someOtherKey?: number;
requiredKey: number[];
nullableKey?: number | null;
multipleTypesKey?: boolean | number;
enumKey?: 枚举值;
}
// 验证请求
export const 请求验证模式 = api(
{expose: true, method: "POST", path: "/validate"},
(data: 请求验证模式): { message: string } => {
console.log(data);
return {message: "验证通过了"};
},
);
进入全屏模式 或 退出全屏模式
Encore使创建和调用服务变得非常简单。从代码角度来看,一个服务只是你仓库中的另一个文件夹。当你调用服务中的一个端点时,这就像调用一个普通的函数一样。更酷的是,实际上这些函数调用会在后台被转换为真正的 HTTP 请求。部署时,你可以选择将服务部署到不同的实例,比如 Kubernetes 集群中,而所有服务代码仍保留在同一个仓库里。
导入一个服务后,像调用普通函数一样调用 API 端点
import { api } from "encore.dev/api";
import { hello } from "~encore/clients"; // 导入 'hello' 服务模块
export const myOtherAPI = api({}, async (): Promise<void> => {
// 通过函数调用 ping 端点
const resp = await hello.ping({ name: "World" });
console.log(resp.message); // '你好,世界!'
});
点击全屏 结束全屏
使用 Encore.ts,你可以将基础设施作为类型安全的对象进行集成到应用代码中。创建一个数据库或 Pub/Sub 主题只需几行应用代码。Encore.ts 会将你的应用打包成 Docker 镜像,在部署时,你只需提供运行时配置,就这样。
用一行代码创建PostgreSQL数据库
import { SQLDatabase } from "encore.dev/storage/sqldb";
const db = new SQLDatabase("userdb", {migrations: "./migrations"});
// ... 例如,使用 db.query 进行数据库查询。
进入全屏模式。退出全屏模式。
创建主题并订阅它们
import { Topic } from "encore.dev/pubsub";
export interface User { // 属性...
}
const signups = new Topic<User>("signup", {
deliveryGuarantee: "at-least-once",
});
await signups.publish({ ... });
全屏。退出全屏。
此外,Encore 还自带一个 内置开发控制台。启动 Encore 应用时,开发控制台可以在 localhost:9400 访问。你可以在这里调用你的端点,有点像 Postman。每次调用你的应用都会生成一个追踪,你可以查看追踪以了解 API 请求、数据库调用和 Pub/Sub 消息。本地开发控制台还提供 自动 API 文档 和 实时更新的架构图。
本地发展动态板
值得一提的是,尽管Encore拥有许多功能,却没有任何npm依赖。
霍诺
Hono 是由 Yusuke Wada 创建的。他于 2021 年启动了该项目,因为当时没有适合 Cloudflare Workers 的好 Node.js 框架,这让他感到不满。从那时起,Hono 逐步增加了对 Node.js、Bun 和 Deno 等运行时的支持。
// 导入Hono库
import { Hono } from 'hono'
// 创建一个新的Hono应用
const app = new Hono()
// 当用户访问根路径时,返回'Hono!'文本
app.get('/', (c) => c.text('Hono!'))
// 导出应用
export default app
全屏切换:进入,退出
Hono真的很小,hono/tiny
预设的大小只有不到13KB,非常适合部署到Cloudflare Workers上。Hono没有任何NPM依赖项,这真的太棒了!
多运行时的卖点真的很有趣,无论你用哪种 JavaScript 运行时,都可以运行 Hono。在他们的仓库里,有一个称为适配器的概念,你可以看到他们为每个运行时做的调整。这确实对吸引用户和扩展用户基数有很大帮助,但对于单个用户来说,一旦你的应用部署到云端,通常不会去切换运行时。
虽然 Hono 本身很轻量,它也有一系列的 中间件 ,既有第一方的也有第三方的,你可以安装这些中间件来增强你的应用。这种“需要时再添加”的方法与 Express 流行的做法类似。这种做法在应用较小的时候效果很好,但随着应用变大,维护一个庞大的依赖列表可能会变得相当麻烦。
艾丽西娅
Elysia,就像 Encore 一样,是 专为 TypeScript 构建的,并且在你的 API 处理程序中也提供类型安全性。了解你在 API 处理程序中处理的内容可以节省大量时间,而且无需在代码中添加各种类型检查真是太好了。
你通过 t
模块指定类型,该模块是 TypeBox 验证库 的一个扩展。与 Encore 不同,, 验证发生在 JavaScript 层,这会带来一些性能损耗。
import { Elysia, t } from 'elysia'
new Elysia()
.patch("/profile", ({ body }) => body.profile, {
body: t.Object({
id: t.Number(),
profile: t.File({ type: "image" }),
}),
})
.listen(3000);
进入全屏,退出全屏
添加Swagger文档只需一行代码,Elysia内置支持OpenTelemetry,这确实很方便,因此你可以轻松地监控你的应用,无需考虑平台。
艾莉西娅很快啊!但没有恩科拉那么快,如你将在下一节看到的。
Nest.js (一个基于 Node.js 的框架)
Nest.js 与其他用于比较的框架有些不同。Encore、Elysia 和 Hono 提供极简的 API 用于创建端点和中间件,并且你可以自由地按照自己的喜好来构建业务逻辑。而 Nest.js 则更加有自己的主张,强制你以某种特定的方式来组织代码。它提供了一种模块化架构,将代码组织成不同的抽象部分,例如提供者、控制器、模块和中间件。
Nest 设计目的是为了使更大的应用程序的维护和开发变得更加容易。但是否喜欢 Nest 提供的这种有倾向性的结构,最终这完全取决于个人的看法。在我看来,对于那些长期可维护性比速度和简洁性更重要的大规模项目来说,使用 Nest 可能更有优势。但对于只有少数开发者的较小项目来说,这种额外的抽象层次在大多数情况下可能是多余的。与 Hono、Encore 和 Elysia 这些框架相比,Nest 的这种倾向性也会导致一个更陡峭的学习曲线。
[]
在使用 Nest 时,你可以选择使用 Express 或 Fastify 作为 Nest 底层的 HTTP 服务器框架。所有 Nest 的功能都是在其之上构建的。
性能速度可能不是选择框架时最重要的考量,但也不能完全忽视。它会影响应用的响应速度,进而影响你的托管费用。
我们分别在有和没有请求验证的情况下进行了性能测试,测量单位为每秒请求数。括号中的名称为所使用的请求验证库(如果有),而Encore.ts自带请求验证。
Encore.ts 在我们进行的基准测试中是所有框架中速度最快的,其次是 Elysia、Hono、Fastify 和 Express。因为 Nest 在底层使用的是 Fastify 或 Express,所以 Nest 应用的性能应该差不多,不过由于 Nest 会增加一些额外的开销,所以可能会稍微慢一点。
Encore.ts怎么可以这么快呀?秘诀就在于Rust运行时,Rust确实很快!
Encore.ts实际上分为两部分。
面向用户的 TypeScript 部分,用于定义 API 和基础架构。
- 内部有一个用 Rust 语言编写的支持多线程的运行时。
性能提升的关键是从 Node.js 的单线程事件循环中转移尽可能多的任务到 Rust 的运行时。
例如,Rust 运行时负责处理所有的输入输出,比如接收传入的 HTTP 请求或从数据库读取数据。一旦这些请求或查询被完全处理,就会交给 Node.js 的事件循环。
代码结构Hono、Elysia 和 Encore 对你如何编写代码没有严格规定。并且它们在创建 API 和中间件的过程中也非常相似。
这里为每个框架提供了一个GET端点。当然存在一些差异,但仔细一看,这些API看起来相当相似。至少在我看来,这不应成为决定性因素。
Encore.ts (一个文件名)
interface Response {
message: string;
}
export const get = api(
{ expose: true, method: "GET", path: "/hello/:name" },
async ({ name }: { name: string }): Promise<Response> => {
const msg = `Hello ${name}!`;
return { message: msg };
},
);
进入全屏 退出全屏
艾莉西亚
import { Elysia, t } from "elysia";
new Elysia()
.get(
"/hello/:name",
({ params }) => {
const msg = `你好 ${params.name}!`;
return { message: msg };
},
{
response: t.Object({
message: t.String(),
}),
},
)
进入全屏,退出全屏
霍诺
import { Hono } from "hono";
const app = new Hono();
app.get("/hello/:name", async (c) => {
// 这里我们回应一个问候信息给用户
const msg = `你好 ${c.req.param("name")}!`;
return c.json({ message: msg });
});
全屏查看,退出全屏
真正起关键作用的是在构建坚固的应用程序时能够依赖类型安全。Encore 和 Elysia 提供类型安全的 API,而 Encore 更进一步,在与基础架构如 Pub/Sub 交互时提供编译时的类型安全。使用 Encore 调用另一个服务的端点时也能获得编译时的类型安全。如果你之前曾接触过微服务架构,你一定知道这有多重要。
GIF
Nest.js 在 API 设计上确实非常出色。Nest 应用中有许多概念和抽象,这完全看你的喜好如何。当你查看一个 Nest 应用时,你会发现一个显眼的特点就是它大量使用了 装饰器。Nest 非常依赖于装饰器,例如,在使用依赖注入将服务插入控制器时。
巢控
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
/**
* @Controller() 注解用于定义 NestJS 控制器。
*/
@Controller()
export class AppController {
/**
* 构造函数注入 AppService 实例。
*/
constructor(private readonly appService: AppService) {}
/**
* @Get() 装饰器定义了一个 HTTP GET 请求处理函数。
*/
@Get()
getHello(): string {
return this.appService.getHello();
}
}
全屏模式 退出全屏
说实话,我对这个没太多感觉,我相信我也不是唯一一个这么想的。
部署和基础设施这些框架都很相似,都可以部署到所有主流的云平台(例如 Digital Ocean 和 Fly.io),或者直接部署到像 AWS 或 GCP 这样的服务商。
Encore 提供自动本地基础设施。运行 encore run
可启动你的应用,并自动搭建所有本地基础设施,如数据库和 Pub/Sub 主题。告别那些 YAML、Docker Compose 等常见麻烦。
GIF,再跑一次
在构建Encore应用时,你可以获得一个运行时配置文件,用于提供连接你云中基础设施所需的配置信息。
如果你想快速将Encore应用部署到云端,又不想自己动手的话,你可以使用Encore Cloud。Encore Cloud 提供 CI/CD 和 pull request 的预览环境。当然,你也可以选择在你的 AWS 或 GCP 上自动为你配置所有必要的基础设施。这意味着你的应用不再受制于任何第三方服务,你对所有基础设施都有完全的控制权。
Hono 的特别之处在于它支持多种不同的运行时,因此在部署方面你有很多选择。你可以轻松地部署到 Cloudflare Workers、Netlify 或 AWS Lambda,且几乎不需要任何配置。
使用 Nest 时,你可以运行 nest build
命令来将你的 TypeScript 代码编译成 JavaScript。该过程会生成一个包含编译文件的 dist
目录。基本上就这样,然后你可以用 Node.js 来运行 dist
文件夹中的代码。
推荐的部署 Elysia 应用方式是使用 bun build
命令将你的应用编译成二进制。一旦编译成二进制,运行服务器时就不需要在机器上安装 Bun
了。
就这样吧!希望你知道下一个项目该用什么框架了。
如果你想了解更多关于Encore.ts的内容,可以在GitHub上查看这个开源项目:https://github.com/encoredev/encore
推荐:
## NestJS 与 Encore.ts:选择适合您 TypeScript 微服务架构的正确框架 Marcus Kohlberg 发布于 Encore ・ Oct 30 #javascript #typescript #微服务架构 #webdev
## 如何使用Docker和Encore在DigitalOcean (数字海洋) 上部署后端应用 Marcus Kohlberg 发布于 Encore ・ 2023年10月 #开源项目 #javascript #docker #go
## 选择合适的技术栈:开发人员指南 Lucas Lima do Nascimento 供 Encore · 2021年10月28日 #新手 #Web 开发 #教程 #职业
共同学习,写下你的评论
评论加载中...
作者其他优质文章