Node、Nest、Next 的入口与契约
沿真实请求链连接模块、校验、授权、处理、响应以及 OpenAPI 与生成客户端。
请求先经过哪些对象
一个 HTTP 入口需要路由、输入解析、主体检查、业务处理和响应。中间件、guard、pipe、interceptor 与 filter 在 Nest 中分别提供请求链上的能力,具体顺序与作用范围由框架和注册位置决定。控制器可保持运输与映射责任,应用服务组织业务效果,外部依赖由可说明的接口连接。
依赖注入通过 token 找到 provider,provider 的模块可见性决定哪些消费者能取得它。单例、请求作用域与瞬时对象具有不同生命周期;引入请求作用域可能影响依赖链与成本。拥有连接或订阅的 provider 还需在停机时释放资源。
Express 和 Fastify 是不同 HTTP 适配器。请求、响应、上传、插件、中间件和测试入口都可能不同,迁移时沿所用能力逐项核对。Nest 官方上传说明明确传统 Multer 路径与 Fastify 不兼容,换一个 import 无法完成上传迁移。Nest 文件上传
用一个公开的标题预览接口可以看清依赖接线,不涉及登录或持久写入。下例采用 Nest 的类 DTO、class-validator 和 Swagger;项目需要相应依赖及 Nest 所需的装饰器元数据配置。
import {
Body, Controller, HttpCode, Injectable, Module, Post,
} from '@nestjs/common';
import { ApiOkResponse, ApiProperty } from '@nestjs/swagger';
import { IsString, Length } from 'class-validator';
import { Transform } from 'class-transformer';
class PreviewInput {
@ApiProperty({ minLength: 1, maxLength: 200 })
@Transform(({ value }) => typeof value === 'string' ? value.trim() : value)
@IsString()
@Length(1, 200)
title!: string;
}
class PreviewOutput {
@ApiProperty()
title!: string;
}
@Injectable()
class PreviewService {
preview(input: PreviewInput): PreviewOutput {
return { title: input.title.trim() };
}
}
@Controller('titles')
class TitlesController {
constructor(private readonly service: PreviewService) {}
@Post('preview')
@HttpCode(200)
@ApiOkResponse({ type: PreviewOutput })
preview(@Body() input: PreviewInput) {
return this.service.preview(input);
}
}
@Module({ controllers: [TitlesController], providers: [PreviewService] })
export class AppModule {}import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFile } from 'node:fs/promises';
import { AppModule } from './app.module.js';
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
validationError: { target: false, value: false },
}));
const options = new DocumentBuilder().setTitle('Preview API').setVersion('1').build();
const spec = SwaggerModule.createDocument(app, options);
await writeFile('openapi.json', JSON.stringify(spec, null, 2));
app.enableShutdownHooks();
await app.listen(3000);模块注册使控制器和服务可取得,pipe 依据运行时 DTO 装饰器校验,控制器调用服务,Swagger 依据显式响应声明生成描述。transform 先裁剪字符串,长度检查据规范化后的值执行;描述中还应说明这一规范化规则。非字符串保留给校验拒绝。此接口未运行新服务。Nest 校验接口、Nest OpenAPI
框架端点怎样映射业务
Next Route Handler 提供 HTTP 接口,Server Action 提供框架管理的服务器动作调用。服务器组件可以在受控执行环境取得数据,再向客户端返回允许传输的信息。页面隐藏和客户端判断不能替代动作中的授权。Next 服务器与客户端组件
一个受保护动作至少读取未知输入、取得主体、校验结构与业务条件、确认对象权限、提交效果和返回受控结果。顺序应符合资源与信息公开要求。可以先拒绝未登录主体,再解析可接受数据;业务条件与权限还需与实际写入保持一致。
type RenameResult =
| { kind: 'renamed'; id: string; version: number }
| { kind: 'rejected'; code: 'INPUT' | 'FORBIDDEN' | 'CONFLICT' };这是返回契约的教学表示:调用方据标签反馈,服务端实现仍需使 version 和提交结果真实对应。返回必要字段,避免整行 ORM 对象和内部堆栈自动进入客户端。
开放接口需要怎样的描述
OpenAPI 记录路径、操作、参数、请求与响应结构,以及声明的安全方案。描述可能手写,也可能从代码和 schema 生成。生成成功只确认生成路径能够工作,遗漏错误响应、权限或序列化差异仍可能让文档失真。
DTO 和 schema 必须同时服务实际运行与描述的目标。TypeScript 类型信息可能被擦除;装饰器、插件或转换器只能映射其支持内容。OpenAPI 3.0 与 3.1 的可空和 JSON Schema 语义不同,应使文档版本与转换输出一致。OpenAPI Specification
orval 可以生成客户端与相关接线,openapi-typescript 与 openapi-fetch 偏向类型描述和请求访问。生成类型不能验证真实网络数据;必要运行解析由可核对的 schema 和边界承担。tRPC 共享类型与调用关系也需实际输入解析,且依赖 TS 消费范围。
对于分离式客户端,可以从上述 spec 生成 paths 类型,再用请求库调用。实际工作流把 openapi.json 当作由服务代码重建的产物,生成命令在检查环境中运行,产物归属和是否入库由项目决定:
pnpm exec openapi-typescript ./openapi.json -o ./src/generated/api.d.tsimport createClient from 'openapi-fetch';
import { z } from 'zod';
import type { paths } from './generated/api';
const api = createClient<paths>({ baseUrl: 'https://api.example.invalid' });
const previewSchema = z.object({ title: z.string() });
export async function previewTitle(title: string) {
const { data, error, response } = await api.POST('/titles/preview', {
body: { title },
});
if (!response.ok) throw new Error(`Preview rejected: ${response.status}`);
if (error !== undefined) throw new Error('Unexpected error response');
return previewSchema.parse(data);
}这里的域名是不可用的示例占位,实际应用换成受控配置并接会话或令牌接口。生成类型使路径与请求体可检查,Zod 解析负责实际响应;错误响应也需在 spec 中声明并映射为产品错误。orval、Hey API、Kubb 或 OpenAPI Generator 可以承担不同生成目标,选择后仍要保留手写业务层与可重建来源。openapi-fetch 使用方式
怎样让契约变化可见
选择主要事实来源,生成描述及客户端,再查看差异并执行生产者消费者的检查。新加字段、收紧范围、改变可空性或错误语义可能影响旧消费者。保存一个 spec 并不能自动保持实现同步;CI 应实际执行相应生成与差异检查。
操作 ID 需要稳定且唯一,不能仅用可能重复的方法名。共享包通过公开出口连接,不读取其他应用内部文件;生成物从源重新生成,业务映射放在明确手写层。
调用路径的验证应包括合法、非法、未授权、冲突和依赖失败。Mock provider 可以确认局部映射,完整 HTTP 测试还需真实注册的 guard、pipe 和适配器;每项结果按实际替换边界报告。
最后更新于