NestJS 还是 Express?按项目大小选就够了
小服务、原型、Webhook 优先 Express;多人长期维护的业务后端优先 NestJS。 默认仍是 Express 适配器,只有压测确认 HTTP 层是瓶颈时再换 Fastify。
约二千一百字·读约七分钟 · English
NestJS 和 Express 总会被放到一起比。常见说法是 NestJS「重」、Express「轻」。方向没错,但只看轻重,选不出该用哪个。
真正要问的是:这些基础设施,你想自己搭,还是用框架给的那一套。Express 只负责 HTTP 路由和中间件;参数对不对、出错怎么返回、文件怎么放、文档谁来写、测试时怎么换掉数据库,都得项目自己定。NestJS 把这些事收进一套固定结构里,多人协作时更容易对齐,代码也更容易写得一致。
NestJS 默认就用 Express
先把这个关系说清楚。NestJS 默认运行在 Express 之上。它没有替换 Express,而是在外面加了模块、依赖注入、控制器、验证和测试等能力。需要更高 HTTP 吞吐时,还可以换成 Fastify。
实际选型时,可以把它们看成三种方案。
| 方案 | 写代码时的感受 | 常见用途 |
|---|---|---|
| 原生 Express | 路由和中间件自己拼,代码少,决定也多 | 小型 API、Webhook、内部工具、原型 |
| NestJS + Express | 仍然用 Express 生态,但多了固定的项目结构 | 业务后台、BFF、多人协作项目 |
| NestJS + Fastify | 保留 Nest 的结构,同时换更快的 HTTP 引擎 | 已确认 HTTP 热路径是瓶颈的服务 |
选 NestJS,并不等于丢掉 Express 那一套中间件和工具。多数时候,请求还是 Express 在处理。差别在于:业务代码放哪、鉴权和日志这类公共逻辑放哪、模块之间怎么互相调用,Nest 都给了固定位置。
Express 适合什么项目
Express 的代码很直接。请求进来后,你注册中间件、匹配路由、执行业务逻辑,然后返回结果。没有太多额外概念。
这让它特别适合小服务。比如一个支付回调、一个数据转发接口、一个内部管理脚本,或者一个还在验证想法的 API。你知道自己要做什么,也不需要先理解 module、provider、guard、pipe 这些词。
代价也很清楚。项目一旦变大,很多事情需要自己定规矩。参数校验放在哪。错误格式怎么统一。日志怎么打。鉴权如何接入。接口文档怎么生成。测试时怎样替换数据库和第三方服务。
Express 不会替你决定。这对有成熟脚手架的团队是优点。团队已经有自己的 starter,里面包含校验、日志、鉴权、OpenAPI、错误码、测试和部署模板,继续用 Express 很合理。没有这些基础时,项目会在几个月后开始付出维护成本。
NestJS 解决的不是 HTTP,而是协作问题
NestJS 的核心价值在项目组织。它会把业务切成 module。每个 module 放自己的 controller、service 和依赖。服务通过依赖注入取得数据库、缓存或第三方客户端。

拿电商后台举例。用户、订单、支付可以各自放进一个模块。订单模块需要哪些服务,向外提供哪些能力,代码里会更清楚。认证、参数校验、日志、异常处理也有固定的入口。
这在一个人写的小项目里不一定有价值。人数一多,情况就不同了。有人改订单接口,有人做支付回调,有人维护后台页面。大家遵循同一套结构,才能减少「这个逻辑到底该放哪」这类讨论。
NestJS 也有学习成本。开发者需要理解 module、provider、scope 和请求生命周期。把模块拆得太细,或者到处使用 request-scoped provider,同样会让代码难读、请求变慢。Nest 帮你定了位置,不会替你做所有设计决定。
性能差异到底有多大
我用同一个轻量接口做了本地微基准。接口只有一个 GET /ping,返回一段很小的 JSON。每种方案跑 3 轮。每轮先预热 3 秒,再以 100 个 keep-alive 并发连接测试 10 秒。

| 方案 | 平均吞吐量 | 平均延迟 | 这组数据说明什么 |
|---|---|---|---|
| Express 5 | 6,666 req/s | 14.5 ms | 本次测试的基线 |
| NestJS + Express | 5,251 req/s | 18.6 ms | 极轻请求下,Nest 的路由和生命周期会带来额外开销 |
| NestJS + Fastify | 27,746 req/s | 3.1 ms | Fastify 的 HTTP 路径更快,提升主要来自底层引擎 |
数据很直观。接口几乎不做业务时,NestJS + Express 比原生 Express 慢一些。这不奇怪。Nest 多做了路由解析、元数据读取和生命周期编排。
但别因此下结论说「Nest 不适合高并发」。真实接口通常卡在数据库、Redis、第三方 API、JSON 序列化、文件处理和网络传输。日志写得不对、CPU 任务堵住事件循环、没有缓存、只跑一个进程,也都会拖慢接口。
这张图只说明一件事:请求特别简单时,框架自己会多花多少时间。 它说明不了线上能扛多少量。真正做决定前,最好在接近线上的环境里压真实接口,把登录校验、查数据、缓存、调外部服务和完整返回都算进去。
别只盯着 QPS
业务项目真正耗时间的,往往不是框架快不快。更常见的是:参数没检查、出错返回格式不统一、文档跟不上代码、测试不好写、新人找不到业务逻辑在哪。
NestJS 给这些事准备了一套默认做法。检查参数可以放在同一个入口,文档可以从代码生成,测试时也能方便地换成假的支付服务或数据库。它的好处不在于第一天写得最短,而在于半年后还比较好改。
Express 也能做到这些。你可以接 Zod、Joi 这类校验库,写一个统一处理错误的中间件,用 Supertest 测接口,用 OpenAPI 管文档。只是这些要团队自己拼起来,并且长期守住规矩。
安全也一样。Nest 的 guard、pipe 和 filter 让安全相关的逻辑有统一入口,但它不会自动帮你处理 HTTPS、Cookie、依赖漏洞、暴力破解或谁能访问什么。不管选哪个框架,这些工作都得自己做。
用这三条规则做决定
| 你的项目情况 | 更合适的选择 | 原因 |
|---|---|---|
| 接口少,服务很薄,生命周期短 | Express | 直接写,部署和调试都轻松 |
| 业务会持续扩展,多人协作,前端依赖稳定接口 | NestJS + Express | 模块、验证、测试和文档更容易保持一致 |
| 真实压测显示 HTTP 层是主要瓶颈 | NestJS + Fastify | 保住 Nest 的工程结构,再优化底层 HTTP 路径 |
还有一种特殊情况。如果团队已经有成熟的 Express 平台,统一验证、日志、鉴权、文档、测试和部署都做齐了,就继续用 Express。NestJS 的许多价值,已经在你们的基础设施中实现了。为了换框架而换框架,没有必要。
如果是一个新项目,预计要维护两三年,也会有多人参与,我会从 NestJS + TypeScript + 默认 Express 适配器 开始。它不一定让你今天写得最快,但会让团队以后少花时间处理重复的基础问题。
最后
Express 和 NestJS 都能写出靠得住的后端。框架本身很少会单独把项目搞砸。没有测试、文档对不上代码、出错返回不统一、也不知道接口大概能扛多少,才更容易让项目失控。
Express 更自由,但团队得自己定规矩并守住。NestJS 结构更清楚,但也不要拆太细、用过头。选框架时,先看服务以后会做多大、多少人会维护、团队已经有哪些现成的基础。答案通常就在这三个问题里。
文中的性能图是在本地做的小测试,只用来比较请求特别简单时框架会多花多少时间,说明不了线上能扛多少量。真正做决定前,请用真实的外部服务、真实的返回内容、你真正要扛的并发,以及实际部署环境,去压关键接口。
Mttao GitHub ↗
探索技术与生活的智慧