跳到正文
cccskills
免费在 GitHub 公开

api-design-conventions

Use when you design or change an endpoint's errors, status codes, pagination, idempotency or concurrency behaviour — one error shape, the status-code table, bounded lists, safe retries

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md5.4 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

API Design Conventions

Overview

Most backend-caused QA bounces are inconsistency, not bugs: a 422 here and a 400 there, an unbounded list, a create that isn't safe to retry. This skill is the shared vocabulary so every endpoint in a service answers these the same way.

Core principle: the repository's existing choice wins (rule repo-conventions-win); these are the defaults for a new repo or an endpoint with nothing established yet.

One error shape

Default for a repo with no convention yet: RFC 9457 application/problem+json — {type, title, status, detail, instance} plus an extension array for field errors, e.g. errors: [{field, message}]. Never a bare string, never a stack trace, never a raw framework exception body. Map it in ONE place (fiber-rest-api's central ErrorHandler, Spring @RestControllerAdvice + ProblemDetail/spring.mvc.problemdetails.enabled=true, Quarkus @ServerExceptionMapper) — not per handler.

Status code table

CodeWhenNote
200 / 201success / created201 includes Location of the new resource
204success with no body (e.g. delete)
400unparseable or invalid inputor 422 if the repo already standardizes on it — don't introduce a second convention
401 / 403unauthenticated / forbidden
404missing, OR present-but-not-yours (BOLA, api-security-checklist)
409duplicate or invalid state transitione.g. a unique-constraint violation (pgx 23505)
412a conditional request's precondition failedIf-Match version mismatch
413body too large
429rate-limitedinclude Retry-After
500genuinely unexpected onlynever for validation or not-found

The task's acceptance criteria or an existing contract always wins over this table.

Mapping database errors (Go, pgx)

Do the translation in the adapter, never in the handler:

switch {
case errors.Is(err, pgx.ErrNoRows):
    return domain.ErrNotFound
case isPGCode(err, "23505"):          // unique_violation
    return domain.ErrConflict
case isPGCode(err, "23503"):          // foreign_key_violation
    return domain.ErrInvalidReference
}

Pagination

  • Every list endpoint has a default page size (e.g. 20–50) and a enforced maximum (e.g. 100) — clamp silently or reject with 400, whichever the repo already does.
  • Deterministic order: ORDER BY created_at DESC, id DESC (a tiebreaker column prevents duplicate/missing rows across pages when timestamps collide).
  • Prefer keyset pagination over offset for anything that can grow past a few thousand rows: WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT $3+1, use the extra row to compute has_more, return an opaque next_cursor (base64 of the last row's sort key). Offset pagination (LIMIT/OFFSET) is fine for small, bounded tables only.

Idempotency and concurrency

  • PUT and DELETE are idempotent by construction — calling them twice with the same input produces the same end state.
  • Make POST/create idempotent where duplicates are a real risk: a natural unique key with INSERT ... ON CONFLICT (key) DO NOTHING RETURNING ..., or an Idempotency-Key request header backed by a dedupe table, following the draft semantics: same key + same payload → replay the stored response; same key + different payload → 422; a second request with the same in-flight key → 409; a required key that's missing → 400.
  • Concurrent read-modify-write: optimistic locking with a version column (UPDATE ... SET ..., version = version + 1 WHERE id = $1 AND version = $2; zero rows affected → 409/412) or JPA @Version (java-persistence); or SELECT ... FOR UPDATE inside one transaction when the operation must serialize.

Compatibility

Evolve additively; prefer a new optional field over renaming one in place; readers should ignore fields they don't recognise rather than failing closed (api-contract-openapi has the breaking-change procedure).

Formats

RFC 3339 timestamps in UTC; money as a decimal string or integer minor units, never a float; ids as strings even when they're numeric internally, so a client never silently loses precision.

Worked Example

❌ POST /tasks twice with the same client-generated request → two rows, two 201s
✅ POST /tasks with Idempotency-Key: <uuid> twice → first 201, second replays the same 201 body

❌ GET /tasks?page=50 on a 2M-row table → OFFSET 500000, a sequential scan
✅ GET /tasks?after=<cursor>&limit=50 → keyset WHERE, index-only scan

Common Mistakes

  • Two different error shapes in the same service.
  • A list endpoint with a default page size but no enforced maximum.
  • A create endpoint with no idempotency story on a client that can legitimately retry (mobile, flaky network).
  • Offset pagination on a table that will outgrow a few thousand rows.
  • Money stored/returned as a float.

Red Flags

  • A 500 response body that is actually a validation failure.
  • OFFSET climbing past five digits in a hot path.
  • A version/@Version field present on the entity but never checked in the update query.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

查看 makifbaysal 的全部技能

报告此技能的问题