现代后端框架
选讲Fastify / Nest.js / Hono——找到最适合你的那把刀
🎯 学习目标
学完本章,你将能够:
- 理解 Fastify 的插件系统和 Schema 验证机制
- 掌握 Nest.js 的依赖注入、控制器和服务架构
- 了解 Hono 的轻量级路由和多平台部署能力
- 根据项目需求选择合适的后端框架
为什么还要学别的
Express 够用了,为什么还要看其他框架?
Express 是"够用"的,但它有几个短板:没有内置验证、性能一般、TypeScript 支持靠社区补丁。
现代框架在这些方面做了显著改进。了解它们的思路,能帮你做出更好的技术选型。即使继续用 Express,也能从中学到更好的架构模式。
框架全景
一张表看清四个主流框架的特点
| 框架 | 性能 | TS 支持 | 适用场景 |
|---|---|---|---|
| Express | 中 | 需 @types | 快速原型、学习 |
| Fastify | 高 | 原生支持 | 高性能 API |
| Nest.js | 中 | 一流 | 大型企业应用 |
| Hono | 极高 | 原生支持 | Edge / Serverless |
选择建议:学习用 Express,新项目用 Fastify 或 Hono,大型企业应用用 Nest.js。
Part 1: Fastify
高性能 Node.js 框架,内置验证与插件生态
Fastify 的核心卖点是比 Express 快 2-3 倍的吞吐量,同时内置了 JSON Schema 验证和原生的 TypeScript 支持。它的插件系统有明确的作用域和生命周期,适合构建结构清晰的中大型 API 服务。
Fastify 路由
类型安全的路由,参数自动推断
$ npm install fastify
import Fastify from "fastify";
const app = Fastify({ logger: true });
// 类型安全路由——泛型定义参数类型
app.get<{ Params: { id: string } }>("/users/:id", async (req, reply) => {
const { id } = req.params; // 自动推断为 string
return { id, name: "张三" };
});
// 带验证的 POST
app.post<{ Body: { name: string; email: string } }>("/users", async (req, reply) => {
const { name, email } = req.body;
return { id: "1", name, email };
});
app.listen({ port: 3000 });
插件系统
用插件扩展 Fastify 的能力,类似 Express 的中间件
import fp from "fastify-plugin";
// 定义插件
export default fp(async (app) => {
app.decorate("db", new Database());
}, { name: "database" });
// 声明插件类型
declare module "fastify" {
interface FastifyInstance {
db: Database;
}
}
// 使用:在任意路由中访问 app.db
app.get("/users", async (req, reply) => {
const users = await app.db.findAll(); // 有类型提示
return users;
});
Fastify 的插件系统比 Express 中间件更强大:插件有明确的作用域(可以限制到某个路由前缀),而且支持异步初始化。
Schema 验证
Fastify 的杀手级特性:用 JSON Schema 同时验证请求和响应
app.post("/users", {
schema: {
body: {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 2 },
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0 }
}
},
response: {
201: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" }
}
}
}
}
}, async (req, reply) => {
reply.code(201);
return { id: "1", name: req.body.name };
});
JSON Schema 验证在 Fastify 中是零成本的——它直接编译成高性能的验证函数,不仅不会拖慢请求,还能让 Fastify 更快地序列化响应。如果你更喜欢 Zod,可以使用 fastify-type-provider-zod 插件来替换默认的 JSON Schema。
请求生命周期 Hooks
在请求的各个阶段插入自定义逻辑:认证、日志、响应改写
// 认证 Hook —— 在所有 /api 路由前执行
app.addHook("onRequest", async (req, reply) => {
const token = req.headers.authorization;
if (!token) {
reply.code(401).send({ error: "未认证" });
return; // 阻止请求继续
}
req.user = verifyToken(token);
});
// 日志 Hook —— 请求完成后记录耗时
app.addHook("onResponse", (req, reply, done) => {
req.log.info({
url: req.url,
method: req.method,
statusCode: reply.statusCode,
responseTime: reply.elapsedTime
}, "请求完成");
done();
});
// 响应改写 Hook —— 统一包装返回格式
app.addHook("preSerialization", async (req, reply, payload) => {
return { success: true, data: payload };
});
Fastify 的 Hook 按生命周期执行:onRequest → preParsing → preValidation → preHandler → preSerialization → onSend → onResponse。你可以精确控制逻辑插入的时机。
Part 2: Nest.js
Angular 风格的企业级框架,装饰器 + 依赖注入 + 模块化
Nest.js 采用装饰器和依赖注入来组织代码,天生适合大型团队协作。它强制模块化架构,内置了守卫(Guard)、拦截器(Interceptor)、管道(Pipe)等概念,让权限控制、日志、数据转换等横切关注点有了标准化的写法。
Nest 架构
三大核心概念:模块、控制器、服务

图:Nest.js 模块化架构层次关系
【AI 生图提示词】请生成一张中文技术架构图,风格简洁、配色柔和、适合教学: - 画面主体:Nest.js 的模块化架构层次图,展示 AppModule 根模块下的子模块及其控制器和服务的组织关系,采用从上到下的树状布局 - 各元素关系:顶层是 AppModule 根模块;AppModule 向下连接两个子模块 UserModule 和 PostModule;UserModule 下包含 UserController 和 UserService,其中 UserController 指向 UserService(表示控制器调用服务);PostModule 下包含 PostController 和 PostService,其中 PostController 指向 PostService - 关键标注:每个节点标注名称:AppModule、UserModule、PostModule、UserController、UserService、PostController、PostService;Controller 调用 Service 的箭头要清晰可辨 - 配色建议:浅蓝/浅灰背景,AppModule 用深蓝色方框,子模块 UserModule 和 PostModule 用中蓝色方框,Controller 用绿色方框,Service 用紫色方框,模块包含关系用实线,控制器调用服务的箭头用橙色标注 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
控制器与服务
装饰器定义路由,服务处理逻辑
$ npm install @nestjs/core @nestjs/common @nestjs/platform-express
// user.controller.ts
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
@Controller("users")
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll(): Promise<User[]> {
return this.userService.findAll();
}
@Get(":id")
findOne(@Param("id") id: string): Promise<User> {
return this.userService.findOne(id);
}
@Post()
create(@Body() dto: CreateUserDto): Promise<User> {
return this.userService.create(dto);
}
}
DTO 验证
用装饰器定义验证规则,请求进来时自动校验
import { IsString, IsEmail, IsOptional, MinLength } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(2)
name!: string;
@IsEmail()
email!: string;
@IsOptional()
@MinLength(6)
password?: string;
}
// user.module.ts —— 组装模块
import { Module } from "@nestjs/common";
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
项目结构与 CLI
Nest CLI 一键生成模块骨架,团队协作更高效
# 安装 CLI 并创建项目
$ npm install -g @nestjs/cli
$ nest new my-project
$ cd my-project
src/
├── app.module.ts # 根模块
├── main.ts # 入口文件
├── common/ # 共享模块
│ ├── guards/ # 守卫
│ ├── interceptors/ # 拦截器
│ └── pipes/ # 管道
└── modules/
├── user/
│ ├── user.module.ts
│ ├── user.controller.ts
│ ├── user.service.ts
│ └── dto/
│ └── create-user.dto.ts
└── post/
├── post.module.ts
├── post.controller.ts
└── post.service.ts
统一的目录结构让团队成员可以快速定位代码。每个模块自包含(controller + service + dto),模块之间通过 exports 显式共享服务,避免隐式依赖。
Guards 守卫
用守卫控制访问权限,比中间件更优雅的权限方案
// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// 从装饰器读取所需角色
const requiredRoles = this.reflector.get(
"roles", context.getHandler()
);
if (!requiredRoles) return true; // 没有标记则放行
const request = context.switchToHttp().getRequest();
const user = request.user;
return requiredRoles.some(role => user.roles?.includes(role));
}
}
// 自定义装饰器
import { SetMetadata } from "@nestjs/common";
export const Roles = (...roles: string[]) => SetMetadata("roles", roles);
// 使用:只有 admin 角色可以访问
@Post("admin/users")
@Roles("admin")
@UseGuards(RolesGuard)
createAdmin(@Body() dto: CreateUserDto) { ... }
Interceptors 拦截器
统一处理响应格式和日志,AOP 编程思想的实践
// 响应格式化拦截器
import { Injectable, NestInterceptor, CallHandler } from "@nestjs/common";
import { map } from "rxjs/operators";
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
return next.handle().pipe(
map(data => ({
success: true,
data,
timestamp: new Date().toISOString()
}))
);
}
}
// 日志拦截器 —— 记录每个请求的耗时
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest();
const now = Date.now();
console.log(`[→] ${request.method} ${request.url}`);
return next.handle().pipe(
tap(() => console.log(`[←] ${Date.now() - now}ms`))
);
}
}
// 全局注册
app.useGlobalInterceptors(
new TransformInterceptor(),
new LoggingInterceptor()
);
依赖注入与模块化
Nest 的 DI 容器自动管理实例的创建和注入
// 1. 定义可注入的服务
import { Injectable } from "@nestjs/common";
@Injectable()
export class UserService {
private users: User[] = [];
async findAll(): Promise<User[]> { return this.users; }
async create(dto: CreateUserDto): Promise<User> { ... }
}
// 2. 组装模块 —— 声明依赖关系
import { Module } from "@nestjs/common";
@Module({
controllers: [UserController], // 处理路由
providers: [UserService], // 提供服务
exports: [UserService], // 允许其他模块使用
})
export class UserModule {}
// 3. 根模块引入子模块
@Module({ imports: [UserModule, PostModule] })
export class AppModule {}
# CLI 快速生成模块、控制器、服务
$ nest g module user
$ nest g controller user
$ nest g service user
Nest 的依赖注入让你不需要手动 new 对象——容器会根据构造函数参数自动注入。模块(Module)则定义了哪些服务对外可见,避免全局状态污染。
Part 3: Hono
极轻量的跨运行时框架,Edge 优先的 Web 开发
Hono 意为"火焰",是一个极轻量的跨运行时框架。同一份代码可以在 Node.js、Bun、Deno、Cloudflare Workers 上运行,包体积仅约 14KB。它内置 RPC 类型推断,让你在前端调用后端 API 时获得完整的类型安全,无需手写类型定义。
Hono 路由
极简 API,上手只需 5 分钟
$ npm install hono
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";
const app = new Hono();
// 中间件
app.use("*", cors());
app.use("*", logger());
// 类型安全路由
app.get("/users/:id", (c) => {
const id = c.req.param("id"); // 自动推断 string
return c.json({ id, name: "张三" });
});
export default app;
Hono 类型安全
通过泛型定义上下文变量,中间件和路由共享类型
interface Env {
Variables: {
userId: string;
};
}
const app = new Hono<Env>();
app.use("/api/*", async (c, next) => {
const token = c.req.header("Authorization");
if (!token) return c.json({ error: "未认证" }, 401);
c.set("userId", verifyToken(token));
await next();
});
app.get("/api/me", (c) => {
const userId = c.get("userId"); // string —— 有类型提示
return c.json({ userId });
});
Hono 最大的优势是跨运行时。如果你的项目可能部署在 Edge(Cloudflare Workers、Vercel Edge Functions),Hono 是最佳选择。
Hono RPC 类型安全
前后端共享路由类型,调用 API 时自动补全参数和返回值
// server.ts —— 定义路由并导出类型
import { Hono } from "hono";
const app = new Hono()
.get("/users", (c) => c.json([{ id: "1", name: "张三" }]))
.post("/users", async (c) => {
const body = await c.req.json();
return c.json({ id: "2", ...body }, 201);
});
export type AppType = typeof app; // 导出路由类型
// client.ts —— 前端调用,自动补全
import { hc } from "hono/client";
import type { AppType } from "./server";
const client = hc<AppType>("/");
// 完整的类型推断!
const res = await client.users.$get();
const users = await res.json(); // { id: string; name: string }[]
const createRes = await client.users.$post({
json: { name: "李四", email: "li@test.com" }
});
Hono RPC 的核心是 hc<> 客户端——它从服务端路由定义自动推断每个 API 的请求参数和响应类型,实现真正的端到端类型安全,类似于 tRPC 的体验但不需要额外协议。
Hono 数据验证
用 Zod 定义 Schema,在中间件层就拦截无效请求
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
import { z } from "zod";
const schema = z.object({
name: z.string().min(2, "姓名至少2个字符"),
email: z.string().email("邮箱格式不正确"),
age: z.number().int().min(0).optional()
});
const app = new Hono()
.post("/users", zValidator("json", schema, (result, c) => {
// 验证失败的回调
if (!result.success) {
return c.json({ error: result.error.flatten() }, 400);
}
}), (c) => {
// 验证通过,数据有完整类型
const { name, email, age } = c.req.valid("json");
return c.json({ id: "1", name, email }, 201);
});
@hono/zod-validator 支持 json、form、query、param 等多种验证目标。验证通过后,c.req.valid("json") 返回的是已经过 Zod 推断的类型安全数据。
中间件链与多平台部署
内置丰富中间件,一行命令部署到 Edge 运行时
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { prettyJSON } from "hono/pretty-json";
import { bearerAuth } from "hono/bearer-auth";
import { cache } from "hono/cache";
const app = new Hono();
// 类型安全的中间件链
app.use("*", logger());
app.use("/api/*", cors());
app.use("/api/*", prettyJSON());
app.use("/api/admin/*", bearerAuth({ token: "secret" }));
// 缓存策略
app.get("/api/posts", cache({ cacheName: "posts", cacheControl: "max-age=60" }),
(c) => c.json(posts)
);
export default app;
# 部署到不同平台
$ npx wrangler deploy # Cloudflare Workers
$ npx vercel deploy # Vercel Edge Functions
$ npx deploy-to-deno # Deno Deploy
Hono 的中间件用 app.use() 按顺序注册,支持路径匹配和通配符。得益于统一的 Web Standard API,同一份代码可以直接部署到 Cloudflare Workers、Vercel Edge、Deno Deploy 等多种平台。
错误处理与超时
Fastify 的错误处理比 Express 更规范:自定义错误类 + 全局处理器
// 自定义业务错误
import { FastifyError } from "fastify";
class NotFoundError extends Error {
statusCode = 404;
constructor(message: string) {
super(message);
this.name = "NotFoundError";
}
}
// 全局错误处理器
app.setErrorHandler((error, req, reply) => {
if (error.validation) {
// Schema 验证失败
reply.code(400).send({
success: false,
error: "请求参数验证失败",
details: error.validation
});
} else if (error instanceof NotFoundError) {
reply.code(404).send({ success: false, error: error.message });
} else {
req.log.error(error);
reply.code(500).send({ success: false, error: "服务器内部错误" });
}
});
// 请求超时保护
app.get("/slow-route", {
config: { timeout: 5000 } // 5秒超时
}, async (req, reply) => { ... });
Fastify 自动区分验证错误(error.validation)和其他错误。结合 FST_ERR_CTP_EMPTY_JSON_BODY 等内置错误码,你可以针对每种情况返回精确的响应。
框架选型指南
不同场景该选哪个框架?一张流程图帮你做决定

图:Node.js 后端框架选型决策流程图
【AI 生图提示词】请生成一张中文技术决策流程图,风格简洁、配色柔和、适合教学: - 画面主体:一个从上到下的决策树流程图,从"项目类型?"判断节点开始,逐步分支到四个框架推荐 - 各元素关系:顶部起始节点"项目类型?"向下分出五个分支判断: - "学习或快速原型?" → 推荐框"Express"(蓝色,标注"文档最多,踩坑成本最低") - "纯 API 且追求高性能?" → 推荐框"Fastify"(绿色,标注"吞吐量最高,内置验证") - "大型企业应用需要团队规范?" → 推荐框"Nest.js"(紫色,标注"DI + 模块化,团队协作规范") - "Edge / Serverless 场景?" → 推荐框"Hono"(橙色,标注"体积最小,跨运行时") - "不确定?" → 推荐框"Fastify"(绿色,标注"性能好、学习曲线平缓") - 关键标注:每个推荐框内标注框架名称和一句话理由;底部用醒目文字总结"选一个用熟比什么都好" - 配色建议:浅灰背景,判断节点用圆角菱形(白色边框),Express 蓝色(#4a90d9),Fastify 绿色(#7ed321),Nest.js 紫色(#9b59b6),Hono 橙色(#f5a623),箭头用深灰色 - 布局方向:从上到下(Top-Down),分支左右对称排列 - 画面比例:16:9 - 避免:过度装饰、3D 效果、文字过小
| 场景 | 推荐框架 | 理由 |
|---|---|---|
| 学习 / 快速原型 | Express | 文档最多,社区最大,踩坑成本最低 |
| 高性能 REST API | Fastify | 吞吐量最高,内置验证,插件生态成熟 |
| 大型企业应用 | Nest.js | 强约束架构,DI + 模块化,团队协作规范 |
| Edge / Serverless | Hono | 体积最小,跨运行时,RPC 类型安全 |
实用建议:如果你不确定选哪个,从 Fastify 开始。它性能好、学习曲线平缓,绝大多数项目都够用。等团队规模变大、需要更强的架构约束时,再考虑迁移到 Nest.js。
常见问题
问题一:Fastify 插件的加载顺序不对
原因:Fastify 插件是异步加载的,依赖关系没有正确声明。
修复:在 fp() 中通过 dependencies 参数声明依赖关系。
问题二:Nest.js 的 DTO 验证不生效
原因:忘记在 main.ts 中启用 ValidationPipe。
修复:app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
问题三:Hono 在 Node.js 中报错
原因:Hono 默认为 Edge 环境,需要额外安装 @hono/node-server。
修复:npm install @hono/node-server,然后用 serve(app) 启动。
试一试:Fastify
练习一
任务:用 Fastify 创建一个 GET /health 接口,返回 { status: "ok" }。
// 提示:Fastify 路由直接 return 对象即可
// 你的代码:
参考答案
import Fastify from "fastify";
const app = Fastify();
app.get("/health", async (req, reply) => {
return { status: "ok" };
});
app.listen({ port: 3000 });
试一试:Hono + Nest
练习二
任务:用 Hono 写一个带认证中间件的路由,只有带 Authorization 头的请求才能访问 /api/me。
// 提示:检查 c.req.header("Authorization")
// 你的代码:
参考答案
const app = new Hono();
app.use("/api/*", async (c, next) => {
const token = c.req.header("Authorization");
if (!token) return c.json({ error: "未认证" }, 401);
await next();
});
app.get("/api/me", (c) => {
return c.json({ user: "Alice" });
});
📝 要点总结
本章核心知识点
Fastify
高性能 + Schema 验证 + 插件系统,适合追求极致性能的 API 服务
Nest.js
依赖注入 + 模块化 + 装饰器,适合大型企业级应用
Hono
轻量 + 类型安全 + 多平台,适合边缘计算和 Serverless 场景
选型原则
没有最好的框架,只有最适合项目规模和团队经验的选择
下一章
框架只是入口,数据才是核心。下一章学习数据持久化——用 Prisma 连接数据库,让数据真正"存下来"。