hono-api-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAPI Design & Standards (Hono + Zod)
API设计与标准(Hono + Zod)
When to Use This Skill
何时使用此技能
- Defining new HTTP endpoints for a bounded context
- Reviewing, auditing, or refactoring existing API routes for consistency
- Establishing API standards for a TypeScript/Clean Architecture backend
- Designing contracts for frontend, SDK, CLI, MCP, or third-party consumers
- Defining or updating API contracts in a shared contracts package (e.g. )
api-contracts - Enforcing consistent route registration and generated OpenAPI docs
- 为限界上下文定义新的HTTP端点
- 审查、审核或重构现有API路由以保证一致性
- 为TypeScript/Clean Architecture后端建立API标准
- 为前端、SDK、CLI、MCP或第三方消费者设计契约
- 在共享契约包(如)中定义或更新API契约
api-contracts - 强制一致的路由注册和生成的OpenAPI文档
Choose the API Style First
先选择API风格
This skill supports two mutually exclusive conventions. Lock onto exactly one per project before designing or auditing any endpoint, and never mix them within the same API surface. The shared core below (Clean Architecture, Zod as source of truth, + , the error helper, generated OpenAPI) applies to both styles. Only paths, methods, parameter placement, and response shapes differ.
createRouteapp.openapi| Style | Shape | Read |
|---|---|---|
| Standard REST | Resource paths + HTTP verbs ( | |
| POST-only action-based | Every endpoint is | |
Determine the style in this order:
- Project docs win. Check , ADRs, and design docs. If they specify a style (or conflict with anything in this skill), the project documents take precedence.
AGENTS.md - Infer from existing routes. Grep for and inspect
createRoute({/method: varied verbs withpathpaths means REST; every route/{id}withmethod: "post"suffixes means POST-only./<action> - Greenfield or ambiguous: ask the user which style the project uses. Default to standard REST for new public APIs unless the project has already standardized on POST-only.
Once chosen, read only that style's reference file and apply it consistently. If a project already uses one style, do not introduce endpoints in the other.
此技能支持两种互斥的约定。在设计或审核任何端点之前,每个项目必须锁定其中一种,且绝不能在同一API范围内混合使用。以下共享核心内容(Clean Architecture、Zod作为唯一可信源、 + 、错误辅助工具、生成的OpenAPI)适用于两种风格。仅路径、方法、参数位置和响应格式有所不同。
createRouteapp.openapi| 风格 | 格式 | 参考文档 |
|---|---|---|
| 标准REST | 资源路径 + HTTP动词( | |
| 仅POST的基于动作 | 每个端点均为 | |
按以下顺序确定风格:
- 项目文档优先。查看、架构决策记录(ADRs)和设计文档。如果它们指定了风格(或与本技能内容冲突),则以项目文档为准。
AGENTS.md - 从现有路由推断。搜索并检查
createRoute({/method:使用多种动词且路径包含path表示REST;所有路由均为/{id}且路径带有method: "post"后缀表示仅POST。/<action> - 新项目或不明确场景:询问用户项目使用哪种风格。对于新的公共API,默认使用标准REST,除非项目已统一采用仅POST风格。
选择后,仅阅读该风格的参考文件并始终如一地应用。如果项目已使用一种风格,则不要引入另一种风格的端点。
Core Principles (both styles)
核心原则(两种风格通用)
- Contracts in a shared package: Request/response schemas live in a central package when shared across backend, frontend, SDKs, or tests.
api-contracts - Zod is the single source of truth: Every schema drives runtime validation, TypeScript types (), and the OpenAPI spec at once. Never duplicate hand-written OpenAPI YAML/JSON.
z.infer - Every endpoint uses +
createRoute: Never register handlers directly withapp.openapi(route, handler),app.get(...), etc., or the endpoint vanishes fromapp.post(...). The/openapi.jsoncall must list every possible response status code increateRoute.responses - Error mapping goes through a single shared helper: Typed application errors map to HTTP in one place (e.g. ). Never inline
mapApplicationErrorToResponse(context, error)in a controller, use case, or route handler. If the helper doesn't cover a new error type, extend the helper — do not branch inline.return context.json({ error, details: { code } }, status) - is generated, never authored: The spec is produced by
openapi.jsonfrom registered schemas and routes. If the spec is wrong, fix the Zod schema, not the spec. Never commit a generated spec.@hono/zod-openapi - Scalar UI consumes the generated spec: Interactive docs render from the same the clients consume. No separate documentation artifact exists.
/openapi.json - Clean Architecture: Controllers are thin adapters over application use cases. Design starts from domain use cases and application DTOs, then maps to HTTP.
- 契约存放在共享包中:当请求/响应模式在后端、前端、SDK或测试之间共享时,应存放在中央包中。
api-contracts - Zod是唯一可信源:每个模式同时驱动运行时验证、TypeScript类型()和OpenAPI规范。切勿重复手写OpenAPI YAML/JSON。
z.infer - 每个端点都使用+
createRoute:绝不要直接使用app.openapi(route, handler)、app.get(...)等注册处理器,否则端点会从app.post(...)中消失。/openapi.json调用必须在createRoute中列出所有可能的响应状态码。responses - 错误映射通过单一共享辅助工具进行:类型化应用程序错误在一处映射到HTTP(例如)。绝不要在控制器、用例或路由处理器中内联
mapApplicationErrorToResponse(context, error),如果辅助工具未涵盖新的错误类型,请扩展辅助工具——不要内联分支处理。return context.json({ error, details: { code } }, status) - 是生成的,而非手写的:规范由
openapi.json从已注册的模式和路由生成。如果规范有误,请修复Zod模式,而非修改规范。绝不要提交生成的规范。@hono/zod-openapi - Scalar UI消费生成的规范:交互式文档从客户端使用的同一渲染。不存在单独的文档工件。
/openapi.json - Clean Architecture:控制器是应用用例之上的轻量适配器。设计从领域用例和应用DTO开始,然后映射到HTTP。
Design Workflow (Top-Down)
设计工作流(自上而下)
- Start from the use case
- Identify the bounded context and use case (e.g. ,
ListCustomers).CreateOrder - Define clear input/output DTOs in the application layer.
- Identify the bounded context and use case (e.g.
- Map to the chosen style
- Apply the path, method, parameter, and response conventions from your style's reference file.
- Decide status codes and error shapes.
- Map HTTP ↔ use case
- The route/controller parses validated HTTP input into application DTOs, calls the use case, and maps the result back to HTTP.
- Apply cross-cutting concerns consistently
- Auth, authorization, validation, logging, idempotency, rate limits, and error handling follow shared helpers.
- 从用例开始
- 识别限界上下文和用例(例如、
ListCustomers)。CreateOrder - 在应用层定义清晰的输入/输出DTO。
- 识别限界上下文和用例(例如
- 映射到所选风格
- 应用所选风格参考文件中的路径、方法、参数和响应约定。
- 确定状态码和错误格式。
- 映射HTTP ↔ 用例
- 路由/控制器将经过验证的HTTP输入解析为应用DTO,调用用例,并将结果映射回HTTP。
- 始终如一地应用横切关注点
- 认证、授权、验证、日志、幂等性、速率限制和错误处理遵循共享辅助工具。
Audit Workflow
审核工作流
When auditing an existing route or API surface, first confirm the project's style, then walk the §API Design Checklist for each endpoint under review and report pass/fail per item — including schema-level concerns like metadata on every request/response schema, not just method/path. A route that uses the right method and shape but inlines Zod schemas (missing ) is still non-compliant and must be flagged. So is any endpoint that uses the wrong style for the project.
.openapi("RefName").openapi("RefName")审核现有路由或API范围时,首先确认项目的风格,然后针对每个待审核端点逐一检查§API设计检查表的各项——包括模式级别的关注点,如每个请求/响应模式上的元数据,而不仅仅是方法/路径。即使路由使用了正确的方法和格式,但内联了Zod模式(缺少),仍不符合规范,必须标记出来。使用与项目风格不符的端点也是如此。
.openapi("RefName").openapi("RefName")Validate Both Directions (request AND response)
双向验证(请求和响应)
createRouteapp.openapiresponsesDo this through one shared response helper that parses the payload through the response schema before sending. A mismatch throws and is caught by the global error handler (mapped to ) rather than shipping a malformed body:
500ts
// src/infra/http/respond.ts — shared, used by every controller in both styles
import type { Context } from "hono";
import type { ContentfulStatusCode } from "hono/utils/http-status";
import type { z } from "@hono/zod-openapi";
export function respond<S extends z.ZodTypeAny>(
context: Context,
schema: S,
payload: z.input<S>,
status: ContentfulStatusCode
) {
return context.json(schema.parse(payload), status); // runtime-validated against the contract
}Keep always-on in non-production and at least sampled in production if the parse cost matters; never skip it entirely, or the response contract is unenforced. Request-side: register a on the instance so failed request validation becomes the shared error envelope () instead of Hono's default body — see .
schema.parsedefaultHookOpenAPIHono400references/openapi-generation.mdcreateRouteapp.openapiresponses通过一个共享的响应辅助工具实现这一点,该工具在发送前通过响应模式解析负载。不匹配会抛出错误并被全局错误处理器捕获(映射为),而不是发送格式错误的请求体:
500ts
// src/infra/http/respond.ts — 共享工具,两种风格的所有控制器均使用
import type { Context } from "hono";
import type { ContentfulStatusCode } from "hono/utils/http-status";
import type { z } from "@hono/zod-openapi";
export function respond<S extends z.ZodTypeAny>(
context: Context,
schema: S,
payload: z.input<S>,
status: ContentfulStatusCode
) {
return context.json(schema.parse(payload), status); // 根据契约进行运行时验证
}在非生产环境中始终启用;如果解析成本重要,在生产环境中至少进行抽样检查;绝不要完全跳过它,否则响应契约将无法强制执行。请求端:在实例上注册,使失败的请求验证变为共享错误信封(),而非Hono的默认请求体——请参阅。
schema.parseOpenAPIHonodefaultHook400references/openapi-generation.mdController Pattern (both styles)
控制器模式(两种风格通用)
Controllers self-register routes via . validates the request — no separate call is needed. On success the controller returns through so the body is validated against the declared response schema; the success shape differs by style (the resource schema directly for REST, the response schema for POST-only). The error path is identical and goes through the shared error helper.
app.openapi(route, handler)createRoutezValidatorrespond(...){ data, message }ts
import type { OpenAPIHono } from "@hono/zod-openapi";
import { someRoute } from "../routes/some.route";
import { SomeResponseSchema } from "@/api-contracts";
import { mapApplicationErrorToResponse } from "@/infra/http/error-handling";
import { respond } from "@/infra/http/respond";
export class SomeController {
constructor(
private readonly app: OpenAPIHono,
private readonly useCase: SomeUseCase
) {
this.app.openapi(someRoute, async context => {
const input = context.req.valid("json"); // or "query" / "param"
const credential = context.get("credential");
const result = await this.useCase.execute({ ...input, workspaceId: credential.workspaceId });
if (!result.ok) return mapApplicationErrorToResponse(context, result.error);
// REST: respond(context, SomeResourceSchema, result.value, 200)
// POST-only: respond(context, SomeResponseSchema, { data: result.value, message: "Success" }, 200)
return respond(context, SomeResponseSchema, result.value, 200);
});
}
}See your style's reference file for the exact success shape and concrete examples.
createRoute控制器通过自行注册路由。会验证请求——无需单独调用。成功时,控制器通过返回,以便请求体根据声明的响应模式进行验证;成功格式因风格而异(REST直接返回资源模式,仅POST返回响应模式)。错误路径相同,均通过共享错误辅助工具处理。
app.openapi(route, handler)createRoutezValidatorrespond(...){ data, message }ts
import type { OpenAPIHono } from "@hono/zod-openapi";
import { someRoute } from "../routes/some.route";
import { SomeResponseSchema } from "@/api-contracts";
import { mapApplicationErrorToResponse } from "@/infra/http/error-handling";
import { respond } from "@/infra/http/respond";
export class SomeController {
constructor(
private readonly app: OpenAPIHono,
private readonly useCase: SomeUseCase
) {
this.app.openapi(someRoute, async context => {
const input = context.req.valid("json"); // 或 "query" / "param"
const credential = context.get("credential");
const result = await this.useCase.execute({ ...input, workspaceId: credential.workspaceId });
if (!result.ok) return mapApplicationErrorToResponse(context, result.error);
// REST: respond(context, SomeResourceSchema, result.value, 200)
// 仅POST: respond(context, SomeResponseSchema, { data: result.value, message: "Success" }, 200)
return respond(context, SomeResponseSchema, result.value, 200);
});
}
}有关确切的成功格式和具体的示例,请参阅所选风格的参考文件。
createRouteCross-Cutting Concerns (both styles)
横切关注点(两种风格通用)
Bake these into the contract, not just the prose — generated SDK/MCP/CLI consumers depend on them being machine-readable.
- Idempotency for unsafe operations. Creation and state-changing operations must be safely retryable — clients and gateways retry on timeouts, and a naive retry double-charges or double-creates. Accept an (REST header, or an
Idempotency-Keybody field in POST-only) on create/state-change endpoints; persist the key with its first result for a dedup window; a replay with the same key returns the original response, the same key with a different payload returnsidempotencyKey. Reads are naturally idempotent and need no key.409 - Versioning & breaking changes. Version the surface from day one (prefix for REST; a version segment or header for POST-only). Within a version make only additive, backward-compatible changes: add optional fields, never remove or repurpose existing ones, never tighten validation on existing inputs. A breaking change requires a new version plus a deprecation path — signal removal with
/v1andDeprecationresponse headers and a migration window. Otherwise generated consumers break silently.Sunset - Rate limiting. Declare and make limits observable: emit
429 Too Many Requests,RateLimit-Limit, andRateLimit-Remainingon responses, plusRateLimit-Reseton aRetry-After, and declare them in the route's429headers so codegen clients can back off. Scope limits per credential/workspace, not per IP, for authenticated APIs.responses
将这些融入契约,而不仅仅是文字说明——生成的SDK/MCP/CLI消费者依赖于它们的机器可读性。
- 不安全操作的幂等性。创建和状态变更操作必须能够安全重试——客户端和网关会在超时后重试,简单的重试可能会导致重复收费或重复创建。在创建/状态变更端点接受(REST为头信息,仅POST为请求体中的
Idempotency-Key字段);在去重窗口内保留密钥及其首次结果;使用相同密钥的重试返回原始响应,使用相同密钥但不同负载的重试返回idempotencyKey。读取操作天然具有幂等性,无需密钥。409 - 版本控制与破坏性变更。从第一天开始对API范围进行版本控制(REST使用前缀;仅POST使用版本段或头信息)。在同一版本内仅进行可向后兼容的增量变更:添加可选字段,绝不要删除或重新使用现有字段,绝不要收紧对现有输入的验证。破坏性变更需要新版本加上弃用路径——使用
/v1和Deprecation响应头信号移除,并提供迁移窗口。否则生成的消费者会静默崩溃。Sunset - 速率限制。声明并使限制可观测:在响应中返回
429 Too Many Requests、RateLimit-Limit和RateLimit-Remaining,在RateLimit-Reset响应中添加429,并在路由的Retry-After头中声明它们,以便代码生成客户端可以退避。对于已认证的API,按凭证/工作区而非IP设置限制范围。responses
Shared Conventions
共享约定
Credential model, authorization semantics, and field formats apply to both styles and are detailed in — read it when shaping contracts, credentials, or money/date fields. Key defaults:
references/api-conventions.md- Credentials carry (what) +
scopes(where). Never infer broad access; whole-tenant access needs an explicit boundary. MCP tokens are a separate credential type. Declare via OpenAPIaccessBoundaryso codegen/MCP tooling can model them.security - Authorization: out-of-scope resource → ; exists-but-forbidden →
404; query filters narrow but never expand a credential's boundary.403 - Field formats: camelCase JSON fields/query params; money as a decimal string + ISO 4217 (never floats); instants ISO 8601 with timezone, business dates ; temporal filters name their dimension (
YYYY-MM-DD, notcreatedFrom).from - Async operations: + a status resource with an explicit state enum, polled via
202 Accepted.GET
凭证模型、授权语义和字段格式适用于两种风格,详情请参阅——在设计契约、凭证或金额/日期字段时阅读此文件。关键默认值:
references/api-conventions.md- 凭证包含(权限范围) +
scopes(访问边界)。绝不要推断广泛的访问权限;全租户访问需要明确的边界。MCP令牌是单独的凭证类型。通过OpenAPIaccessBoundary声明,以便代码生成/MCP工具可以建模。security - 授权:超出范围的资源 → ;资源存在但禁止访问 →
404;查询过滤器只能缩小但绝不能扩大凭证的边界。403 - 字段格式:JSON字段/查询参数使用驼峰式(camelCase);金额为十进制字符串 + ISO 4217(绝不要使用浮点数);时间戳为带时区的ISO 8601格式,业务日期为;时间过滤器需指明维度(如
YYYY-MM-DD,而非createdFrom)。from - 异步操作:返回+ 带有明确状态枚举的状态资源,通过
202 Accepted轮询。GET
Status Codes
状态码
- — successful reads and updates with a body
200 OK - — successful creation; include
201 Createdwhen practicalLocation - — async job accepted (REST resource-style operations)
202 Accepted - — successful delete/revoke/disconnect with no body
204 No Content - — conditional GET with unchanged representation (REST)
304 Not Modified - — malformed input or validation error at the HTTP boundary
400 Bad Request - — missing/invalid authentication
401 Unauthorized - — authenticated but not allowed
403 Forbidden - — resource not found
404 Not Found - — duplicates, state conflicts, business invariants
409 Conflict - — domain validation failure distinct from HTTP validation
422 Unprocessable Entity - — rate limit
429 Too Many Requests - — unhandled errors
500 Internal Server Error
- — 成功读取和更新并返回请求体
200 OK - — 成功创建;尽可能包含
201 Created头Location - — 异步任务已接受(REST资源风格操作)
202 Accepted - — 成功删除/撤销/断开连接且无请求体返回
204 No Content - — 条件GET请求,资源未变更(REST)
304 Not Modified - — HTTP边界处的输入格式错误或验证失败
400 Bad Request - — 缺少/无效的认证信息
401 Unauthorized - — 已认证但无权限
403 Forbidden - — 资源未找到
404 Not Found - — 重复项、状态冲突、业务不变量违反
409 Conflict - — 领域验证失败,与HTTP验证区分开
422 Unprocessable Entity - — 超出速率限制
429 Too Many Requests - — 未处理的错误
500 Internal Server Error
Error Envelope (both styles)
错误信封(两种风格通用)
Errors use the same structured body in both styles. is a human-readable string safe for UI; everything machine-readable lives inside .
errordetailsjson
{
"error": "Human-readable message",
"details": {
"code": "RESOURCE_NOT_FOUND",
"requestId": "req_01HZ..."
}
}Rules:
- — always a string; never an object, never holds the code.
error - — always required; stable, machine-readable,
details.code.SCREAMING_SNAKE_CASE - — required in production for traceability.
details.requestId - Additional optional context goes inside :
details,fieldErrors,resourceId, etc.retryAfter - Never put at the top level, never nest
code, never return free-form error strings.{ error: { code, message } }
两种风格的错误均使用相同的结构化请求体。是适合UI显示的人类可读字符串;所有机器可读内容都在内。
errordetailsjson
{
"error": "人类可读消息",
"details": {
"code": "RESOURCE_NOT_FOUND",
"requestId": "req_01HZ..."
}
}规则:
- — 始终为字符串;绝不要是对象,绝不要包含错误码。
error - — 必须始终存在;稳定、机器可读、采用大写下划线格式(SCREAMING_SNAKE_CASE)。
details.code - — 生产环境中必须存在,用于可追溯性。
details.requestId - 额外的可选上下文放在内:
details、fieldErrors、resourceId等。retryAfter - 绝不要将放在顶层,绝不要嵌套
code,绝不要返回自由格式的错误字符串。{ error: { code, message } }
OpenAPI Spec Generated From Zod
从Zod生成OpenAPI规范
The OpenAPI specification is a build artifact of the Zod schemas, never hand-written.
text
Zod schema → .openapi("RefName") → createRoute({ ... })
→ app.openapi(route, handler) → /openapi.json
→ Scalar UI + generated clientsNon-negotiable rules:
- Every reused schema must call so it appears in
.openapi("RefName").components.schemas - Every endpoint must be declared with and mounted with
createRoute.app.openapi(route, handler) - Every response status code the endpoint can emit must be listed in .
responses - Auth must be declared via OpenAPI , registered once at bootstrap with
security.registerComponent("securitySchemes", ...) - Do not commit generated OpenAPI specs.
For the full walkthrough (schema annotation, bootstrap wiring, client codegen, common mistakes), read . For the route-declaration examples in your convention, read your style's reference file.
references/openapi-generation.mdOpenAPI规范是Zod模式的构建工件,绝不是手写的。
text
Zod schema → .openapi("RefName") → createRoute({ ... })
→ app.openapi(route, handler) → /openapi.json
→ Scalar UI + 生成的客户端不可协商的规则:
- 每个可复用的模式必须调用,使其出现在
.openapi("RefName")中。components.schemas - 每个端点必须用声明,并通过
createRoute挂载。app.openapi(route, handler) - 端点可能返回的每个响应状态码必须在中列出。
responses - 认证必须通过OpenAPI 声明,在启动时通过
security注册一次。registerComponent("securitySchemes", ...) - 不要提交生成的OpenAPI规范。
有关完整的步骤(模式注解、启动配置、客户端代码生成、常见错误),请阅读。有关所选约定的路由声明示例,请阅读对应风格的参考文件。
references/openapi-generation.mdTesting Strategy
测试策略
- Contracts: unit test Zod schemas with valid/invalid payloads.
- Controllers: integration test Hono routes — status codes, auth, validation, error mapping, and the response shape for your style.
- OpenAPI: boot the server, fetch in tests, and verify endpoints, methods, schemas, response codes, and security.
/openapi.json - E2E: exercise key flows through the public API or a generated client.
- 契约:使用有效/无效负载对Zod模式进行单元测试。
- 控制器:对Hono路由进行集成测试——状态码、认证、验证、错误映射以及对应风格的响应格式。
- OpenAPI:启动服务器,在测试中获取,验证端点、方法、模式、响应码和安全设置。
/openapi.json - 端到端(E2E):通过公共API或生成的客户端测试关键流程。
API Design Checklist
API设计检查表
Before merging an API change:
- The endpoint follows the project's chosen style consistently (no mixing REST and POST-only).
- Contracts are defined in the shared package and annotated with .
.openapi("RefName") - Route uses and is mounted with
createRoute, declaring every success and error status code plus the correctapp.openapientry.security - Parameter placement matches the style (REST: path/query/body; POST-only: JSON body only).
- Response shape matches the style (REST: resource directly; POST-only: envelope). Error responses use
{ data, message }.{ error, details: { code } } - Controller is thin, self-registering, and delegates to a use case.
- Typed application errors map through the shared HTTP error helper.
- Auth and authorization are machine-readable in OpenAPI and enforced in code.
- regenerates at boot and reflects the change — verified by fetching the spec.
/openapi.json - Tests cover success, validation failure, auth failure, authorization failure, and domain conflicts.
- Cross-cutting concerns are addressed: unsafe (create/state-change) operations are idempotent via ; the versioning posture is stated with additive-only/breaking-change rules; rate-limit headers (
Idempotency-Key/RateLimit-*) are declared. For a read-only endpoint, note idempotency as N/A rather than omitting it.Retry-After - Shared conventions hold (see ): credentials declare
references/api-conventions.md+scopes; authorization usesaccessBoundaryvs403correctly and filters never expand scope; field formats follow camelCase / money-as-decimal-string + ISO 4217 / ISO 8601 instants /404dates / named temporal filters; async operations returnYYYY-MM-DD+ a status resource.202
合并API变更前:
- 端点始终如一地遵循项目所选的风格(不混合REST和仅POST)。
- 契约定义在共享包中,并使用注解。
.openapi("RefName") - 路由使用并通过
createRoute挂载,声明所有成功和错误状态码以及正确的app.openapi条目。security - 参数位置符合风格要求(REST:路径/查询/请求体;仅POST:仅JSON请求体)。
- 响应格式符合风格要求(REST:直接返回资源;仅POST:信封格式)。错误响应使用
{ data, message }格式。{ error, details: { code } } - 控制器轻量、自注册,并委托给用例。
- 类型化应用程序错误通过共享HTTP错误辅助工具映射。
- 认证和授权在OpenAPI中是机器可读的,并在代码中强制执行。
- 在启动时重新生成并反映变更——通过获取规范进行验证。
/openapi.json - 测试覆盖成功、验证失败、认证失败、授权失败和领域冲突场景。
- 解决了横切关注点:不安全(创建/状态变更)操作通过实现幂等性;版本策略明确(仅增量变更/破坏性变更规则);声明了速率限制头(
Idempotency-Key/RateLimit-*)。对于只读端点,注明幂等性不适用,而非省略。Retry-After - 遵循共享约定(请参阅):凭证声明
references/api-conventions.md+scopes;授权正确使用accessBoundaryvs403,过滤器从不扩大权限范围;字段格式遵循驼峰式 / 金额为十进制字符串 + ISO 4217 / ISO 8601时间戳 /404日期 / 命名时间过滤器;异步操作返回YYYY-MM-DD+ 状态资源。202
Pitfalls (both styles)
常见陷阱(两种风格通用)
- Do not mirror table names blindly; paths represent public product resources, not the database schema.
- Do not bypass OpenAPI route registration with /
app.get(...).app.post(...) - Do not hide auth requirements in free-form text; use
description.security - Do not make breaking response changes without versioning.
- Do not inline error responses; route them through the shared helper.
Style-specific naming rules and pitfalls live in each style's reference file.
- 不要盲目镜像表名;路径代表公共产品资源,而非数据库模式。
- 不要使用/
app.get(...)绕过OpenAPI路由注册。app.post(...) - 不要将认证要求隐藏在自由格式的文本中;使用
description。security - 不要在不进行版本控制的情况下对响应进行破坏性变更。
- 不要内联错误响应;通过共享辅助工具处理。
特定风格的命名规则和陷阱请参阅各风格的参考文件。