Skip to content

平台无关性: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 实现平台无关性的关键点:

  1. 抽象层设计:定义统一的接口规范
  2. 适配器模式:为不同平台实现具体适配器
  3. 工厂模式:动态创建和选择适配器
  4. 统一 API:对外提供一致的使用接口

平台无关性的优势:

  1. 技术选型灵活性:可以根据项目需求选择最适合的平台
  2. 迁移便利性:可以在不同平台间相对容易地迁移
  3. 性能优化:可以选择性能更好的平台
  4. 生态系统利用:可以利用不同平台的生态系统

通过这种设计,NestJS 实现了:

  1. 代码复用:应用逻辑代码无需修改
  2. 平台隔离:底层平台差异被适配器封装
  3. 扩展性:可以轻松添加新的平台支持
  4. 维护性:平台相关代码集中管理

在下一篇文章中,我们将进入第六阶段,探讨模块划分:领域驱动设计(DDD)在 Nest 中的落地,了解 UserModuleOrderModuleSharedModule 的职责边界。