平台无关性:Nest 如何支持 Express 与 Fastify?
NestJS 的一个重要特性是平台无关性,它可以在不同的 HTTP 服务器平台上运行,如 Express 和 Fastify。这种设计使得开发者可以根据项目需求选择最适合的底层平台,同时保持应用代码的一致性。本文将深入探讨 NestJS 如何通过 AbstractHttpAdapter 抽象底层 HTTP 服务器,实现平台无关性。
1. 平台无关性基础概念
1.1 为什么需要平台无关性?
不同的 HTTP 服务器平台有各自的优缺点:
typescript
// Express 的特点
// - 成熟稳定,生态系统丰富
// - 中间件机制完善
// - 学习曲线平缓
// - 性能相对较低
// Fastify 的特点
// - 高性能,低开销
// - 基于 Schema 的验证
// - 插件架构
// - 学习曲线较陡峭1.2 NestJS 的解决方案
NestJS 通过抽象层隔离平台特定的实现:
typescript
// 平台无关的应用创建
// 使用 Express
const app = await NestFactory.create(AppModule);
// 使用 Fastify
const app = await NestFactory.create(AppModule, { bufferLogs: true });
// 或者明确指定适配器
const app = await NestFactory.create(AppModule, new FastifyAdapter());2. AbstractHttpAdapter 抽象类
2.1 核心抽象接口
typescript
// AbstractHttpAdapter 核心接口
export abstract class AbstractHttpAdapter<
TServer = any,
TRequest = any,
TResponse = any
> {
// 服务器实例
protected instance: any;
// 获取底层 HTTP 服务器实例
abstract getHttpServer(): TServer;
// 设置头部
abstract setHeader(response: TResponse, name: string, value: string): any;
// 发送响应
abstract reply(response: TResponse, body: any, statusCode?: number): any;
// 获取请求状态码
abstract status(response: TResponse, statusCode: number): any;
// 结束响应
abstract end(response: TResponse, message?: string): any;
// 获取请求对象
abstract getRequestHostname(request: TRequest): string;
abstract getRequestMethod(request: TRequest): string;
abstract getRequestUrl(request: TRequest): string;
// 路由注册
abstract registerParserMiddleware(): any;
abstract setNotFoundHandler(handler: Function): any;
abstract setErrorHandler(handler: Function): any;
// 生命周期方法
abstract close(): any;
abstract init(): Promise<void>;
abstract listen(port: string | number, callback?: () => void): any;
abstract listen(port: string | number, hostname: string, callback?: () => void): any;
// 中间件注册
abstract use(...args: any[]): any;
abstract get(...args: any[]): any;
abstract post(...args: any[]): any;
abstract put(...args: any[]): any;
abstract delete(...args: any[]): any;
abstract patch(...args: any[]): any;
abstract options(...args: any[]): any;
abstract head(...args: any[]): any;
abstract all(...args: any[]): any;
}2.2 适配器工厂模式
typescript
// 适配器创建工厂
export class ApplicationConfig {
private readonly httpAdapterHost = new HttpAdapterHost();
public createHttpAdapter<T extends AbstractHttpAdapter = any>(
httpServer?: T
): T {
if (httpServer) {
return httpServer;
}
// 根据环境或配置选择默认适配器
try {
// 尝试使用 Express
const { ExpressAdapter } = require('@nestjs/platform-express');
return new ExpressAdapter() as T;
} catch (e) {
try {
// 回退到 Fastify
const { FastifyAdapter } = require('@nestjs/platform-fastify');
return new FastifyAdapter() as T;
} catch (e2) {
throw new Error('No HTTP adapter found. Please install @nestjs/platform-express or @nestjs/platform-fastify');
}
}
}
}3. Express 适配器实现
3.1 ExpressAdapter 基础实现
typescript
// Express 适配器实现
export class ExpressAdapter extends AbstractHttpAdapter {
constructor(instance?: any) {
super();
// 如果没有提供实例,创建默认的 Express 应用
this.instance = instance || express();
}
// 获取 HTTP 服务器实例
public getHttpServer(): any {
return this.httpServer;
}
// 设置响应头部
public setHeader(response: express.Response, name: string, value: string) {
return response.set(name, value);
}
// 发送响应
public reply(response: express.Response, body: any, statusCode?: number) {
if (statusCode) {
response.status(statusCode);
}
if (typeof body === 'object') {
return response.json(body);
}
return response.send(String(body));
}
// 设置状态码
public status(response: express.Response, statusCode: number) {
return response.status(statusCode);
}
// 结束响应
public end(response: express.Response, message?: string) {
return response.end(message);
}
// 路由方法
public get(handler: express.RequestHandler);
public get(path: string, handler: express.RequestHandler);
public get(...args: any[]) {
return this.instance.get(...args);
}
public post(handler: express.RequestHandler);
public post(path: string, handler: express.RequestHandler);
public post(...args: any[]) {
return this.instance.post(...args);
}
// 注册中间件
public use(...args: any[]) {
return this.instance.use(...args);
}
// 获取请求信息
public getRequestHostname(request: express.Request): string {
return request.hostname;
}
public getRequestMethod(request: express.Request): string {
return request.method;
}
public getRequestUrl(request: express.Request): string {
return request.url;
}
// 初始化和监听
public async init() {
// Express 不需要特殊初始化
}
public async listen(port: string | number, callback?: () => void);
public async listen(port: string | number, hostname: string, callback?: () => void);
public async listen(port: any, hostname?: any, callback?: any) {
// 创建 HTTP 服务器
this.httpServer = this.instance.listen(port, hostname, callback);
return this.httpServer;
}
// 关闭服务器
public close() {
if (this.httpServer) {
this.httpServer.close();
}
}
}3.2 Express 中间件集成
typescript
// Express 中间件处理
export class ExpressAdapter extends AbstractHttpAdapter {
// 注册解析中间件
public registerParserMiddleware() {
// 注册 body-parser 中间件
this.use(bodyParser.json());
this.use(bodyParser.urlencoded({ extended: true }));
// 注册 cookie-parser 中间件
this.use(cookieParser());
}
// 设置 404 处理器
public setNotFoundHandler(handler: Function) {
this.use((req: express.Request, res: express.Response) => {
handler(req, res);
});
}
// 设置错误处理器
public setErrorHandler(handler: Function) {
this.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
handler(err, req, res, next);
});
}
}4. Fastify 适配器实现
4.1 FastifyAdapter 基础实现
typescript
// Fastify 适配器实现
export class FastifyAdapter extends AbstractHttpAdapter<
FastifyInstance,
FastifyRequest,
FastifyReply
> {
constructor(
instance?: FastifyInstance | FastifyHttp2SecureOptions<any> | FastifyHttp2Options<any> | FastifyHttpsOptions<any> | FastifyServerOptions
) {
super();
// 如果提供了配置选项,创建 Fastify 实例
if (instance && typeof instance !== 'function') {
const options = instance as FastifyServerOptions;
this.instance = fastify(options);
} else if (typeof instance === 'function') {
// 如果提供了工厂函数
this.instance = instance();
} else {
// 创建默认实例
this.instance = fastify();
}
}
// 获取 HTTP 服务器实例
public getHttpServer(): FastifyInstance {
return this.httpServer;
}
// 设置响应头部
public setHeader(response: FastifyReply, name: string, value: string) {
return response.header(name, value);
}
// 发送响应
public reply(response: FastifyReply, body: any, statusCode?: number) {
if (statusCode) {
response.status(statusCode);
}
return response.send(body);
}
// 设置状态码
public status(response: FastifyReply, statusCode: number) {
return response.status(statusCode);
}
// 结束响应
public end(response: FastifyReply, message?: string) {
return response.send(message);
}
// 路由方法
public get(handler: RequestHandler);
public get(path: string, handler: RequestHandler);
public get(...args: any[]) {
return this.instance.get(...args);
}
public post(handler: RequestHandler);
public post(path: string, handler: RequestHandler);
public post(...args: any[]) {
return this.instance.post(...args);
}
// 注册中间件(Fastify 中称为插件)
public use(...args: any[]) {
// Fastify 使用 register 方法注册插件
if (args.length === 1) {
return this.instance.register(args[0]);
}
const [path, fn] = args;
return this.instance.register(fn, { prefix: path });
}
// 获取请求信息
public getRequestHostname(request: FastifyRequest): string {
return request.hostname;
}
public getRequestMethod(request: FastifyRequest): string {
return request.method;
}
public getRequestUrl(request: FastifyRequest): string {
return request.url;
}
// 初始化和监听
public async init() {
// Fastify 可能需要等待初始化
if (this.instance.ready) {
await this.instance.ready();
}
}
public async listen(port: string | number, callback?: () => void);
public async listen(port: string | number, hostname: string, callback?: () => void);
public async listen(port: any, hostname?: any, callback?: any) {
// Fastify 的监听方法
const options: any = { port: +port };
if (hostname) {
options.host = hostname;
}
this.httpServer = await this.instance.listen(options, callback);
return this.httpServer;
}
// 关闭服务器
public async close() {
if (this.instance) {
await this.instance.close();
}
}
}4.2 Fastify 插件集成
typescript
// Fastify 插件处理
export class FastifyAdapter extends AbstractHttpAdapter {
// 注册解析插件
public registerParserMiddleware() {
// Fastify 内置了 body 解析
// 可以注册额外的插件
this.instance.register(fastifyCookie);
this.instance.register(fastifyMultipart);
}
// 设置 404 处理器
public setNotFoundHandler(handler: Function) {
this.instance.setNotFoundHandler((req, res) => {
handler(req, res);
});
}
// 设置错误处理器
public setErrorHandler(handler: Function) {
this.instance.setErrorHandler((error, req, res) => {
handler(error, req, res);
});
}
// Fastify 特有的方法
public getRouter() {
return this.instance;
}
public register(...args: any[]) {
return this.instance.register(...args);
}
}5. 平台适配的实际应用
5.1 统一路由处理
typescript
// 平台无关的路由处理
@Injectable()
export class RoutesResolver {
constructor(private readonly routerProxy: RouterProxy) {}
public registerRoutes(
routes: RouteInfo[],
applicationRef: AbstractHttpAdapter,
basePath: string,
) {
routes.forEach(route => {
const { path, method, targetCallback } = route;
const fullPath = this.normalizePath(basePath, path);
// 使用抽象适配器注册路由
applicationRef[method.toLowerCase()](
fullPath,
this.routerProxy.createProxy(targetCallback),
);
});
}
private normalizePath(basePath: string, path: string): string {
return `${basePath}${path}`.replace(/\/+/g, '/');
}
}5.2 统一中间件处理
typescript
// 平台无关的中间件处理
@Injectable()
export class MiddlewareModule {
public async registerMiddleware(
applicationRef: AbstractHttpAdapter,
middlewareContainer: MiddlewareContainer,
) {
const configs = middlewareContainer.getConfigurations();
for (const [name, config] of configs) {
// 统一注册中间件,适配器会处理平台差异
applicationRef.use(
config.path,
this.createMiddlewareFactory(config.providers),
);
}
}
private createMiddlewareFactory(providers: MiddlewareConfiguration['providers']) {
return async (req: any, res: any, next: Function) => {
// 中间件逻辑
next();
};
}
}6. 性能对比和选择
6.1 性能测试示例
typescript
// 性能测试配置
// Express 配置
const expressApp = await NestFactory.create(AppModule, new ExpressAdapter());
// Fastify 配置
const fastifyApp = await NestFactory.create(AppModule, new FastifyAdapter({
logger: false,
disableRequestLogging: true,
}));
// 性能对比指标
interface PerformanceMetrics {
requestsPerSecond: number;
latency: number;
memoryUsage: number;
cpuUsage: number;
}
// 基准测试示例
async function runBenchmark(app: INestApplication, requests: number) {
const start = process.hrtime();
const startTime = Date.now();
// 发送大量请求
for (let i = 0; i < requests; i++) {
await fetch('http://localhost:3000/test');
}
const end = process.hrtime(start);
const endTime = Date.now();
return {
requestsPerSecond: requests / ((endTime - startTime) / 1000),
latency: (end[0] * 1e9 + end[1]) / requests,
memoryUsage: process.memoryUsage(),
};
}6.2 平台选择指南
typescript
// 平台选择决策树
class PlatformSelector {
static selectPlatform(options: {
performanceRequirements: 'high' | 'medium' | 'low';
teamExperience: 'express' | 'fastify' | 'both';
ecosystemNeeds: 'rich' | 'minimal';
developmentSpeed: 'fast' | 'thorough';
}): AbstractHttpAdapter {
// 高性能要求 + 团队熟悉 Fastify
if (options.performanceRequirements === 'high' &&
(options.teamExperience === 'fastify' || options.teamExperience === 'both')) {
return new FastifyAdapter();
}
// 丰富的生态系统需求
if (options.ecosystemNeeds === 'rich') {
return new ExpressAdapter();
}
// 默认选择 Express(学习曲线平缓)
return new ExpressAdapter();
}
}7. 自定义适配器
7.1 创建自定义适配器
typescript
// 自定义 HTTP 适配器示例
export class KoaAdapter extends AbstractHttpAdapter {
constructor(instance?: any) {
super();
this.instance = instance || new Koa();
}
// 实现所有抽象方法
public getHttpServer(): any {
return this.httpServer;
}
public setHeader(response: any, name: string, value: string) {
response.set(name, value);
return response;
}
public reply(response: any, body: any, statusCode?: number) {
if (statusCode) {
response.status = statusCode;
}
response.body = body;
return response;
}
// ... 实现其他必需的方法
public async listen(port: string | number, callback?: () => void) {
this.httpServer = this.instance.listen(port, callback);
return this.httpServer;
}
// Koa 特有的方法
public callback() {
return this.instance.callback();
}
}
// 使用自定义适配器
const app = await NestFactory.create(AppModule, new KoaAdapter());8. 总结
NestJS 通过 AbstractHttpAdapter 实现平台无关性的关键点:
- 抽象层设计:定义统一的接口规范
- 适配器模式:为不同平台实现具体适配器
- 工厂模式:动态创建和选择适配器
- 统一 API:对外提供一致的使用接口
平台无关性的优势:
- 技术选型灵活性:可以根据项目需求选择最适合的平台
- 迁移便利性:可以在不同平台间相对容易地迁移
- 性能优化:可以选择性能更好的平台
- 生态系统利用:可以利用不同平台的生态系统
通过这种设计,NestJS 实现了:
- 代码复用:应用逻辑代码无需修改
- 平台隔离:底层平台差异被适配器封装
- 扩展性:可以轻松添加新的平台支持
- 维护性:平台相关代码集中管理
在下一篇文章中,我们将进入第六阶段,探讨模块划分:领域驱动设计(DDD)在 Nest 中的落地,了解 UserModule、OrderModule、SharedModule 的职责边界。