Skip to content

API Development Patterns

Your POST /orders endpoint double-charges customers when the mobile client retries on a flaky connection. Your cursor pagination silently drops rows when two records share a created_at. And the OpenAPI spec you hand-wrote drifted from the code three sprints ago, so the generated client is lying to every frontend dev who trusts it. These are the API bugs that survive code review and surface in production — and they’re exactly where an AI agent earns its keep, because the fix is a known pattern, not a creative act.

This recipe shows the prompts that turn “the AI scaffolded a route” into “the route is idempotent, paginated correctly, and documented from the source of truth.” We use Express + TypeScript + Zod as the anchor stack, but the prompts port cleanly to Fastify, NestJS, or Next.js Route Handlers.

  • A paste-ready prompt that generates idempotency-key middleware so client retries never double-charge.
  • A cursor-pagination prompt with a stable composite sort that doesn’t drop rows.
  • An OpenAPI-3.1-from-handlers prompt that derives the spec from your Zod schemas, so it can’t drift.
  • The three-tool workflow for scaffolding an endpoint (Cursor, Claude Code, Codex) and when each shines.
  • A “When This Breaks” checklist for the failure modes these AI-generated patterns hit in production.

Scaffolding an endpoint across the three tools

Section titled “Scaffolding an endpoint across the three tools”

The first move on any new endpoint is the same: hand the agent your existing route conventions and let it match them. Where the tools differ is how you drive that — inline in the editor, in the terminal, or non-interactively in CI.

Open the router file so it’s in context, then open Agent mode (Cmd/Ctrl + I) and reference the file explicitly:

Using the conventions in @src/routes/users.ts, add a POST /orders route to @src/routes/orders.ts. Validate the body with a Zod schema (items: array of {sku, qty}, idempotencyKey: uuid), return 201 with the created order, and 409 on a duplicate idempotency key. Wire it into the existing error-handling middleware — do not invent new error classes.

Agent mode edits the file in place and shows a diff per file. Use Checkpoints before accepting so you can roll back the whole change if the validation shape is wrong.

Idempotent endpoints: the double-charge fix

Section titled “Idempotent endpoints: the double-charge fix”

A retried POST is the classic distributed-systems trap. The fix is an idempotency-key middleware that records the first response and replays it for any repeat of the same key. This is mechanical enough that the AI gets it right — if you tell it where to store the key and how long to keep it.

When the AI hands this back, audit two things before you trust it. First, the in-flight lock: a naive version stores the response only after the handler finishes, so two simultaneous retries both miss the cache and both charge. The replay entry must be claimed (e.g. SET idem:{key} "processing" NX) before the handler runs. Second, the TTL vs. business window: a 24h TTL means a retry on day two double-charges anyway. Ask the AI to make the window explicit and matched to your client’s retry policy.

Cursor pagination that doesn’t drop rows

Section titled “Cursor pagination that doesn’t drop rows”

Offset pagination (?page=3&limit=20) is fine for an admin table but breaks under writes: insert a row while a user pages and everything shifts. Cursor pagination is the production answer, and the bug AI models reliably introduce is sorting on a non-unique column. If two orders share a created_at, a cursor on created_at alone will skip or repeat rows at the page boundary.

The tell that the AI did this right is the composite predicate. WHERE created_at < $1 is wrong; WHERE (created_at, id) < ($1, $2) is right. If you see OFFSET anywhere in the generated query, reject it — that’s the bug you came here to avoid.

Hand-maintained OpenAPI drifts. The durable fix is to derive the spec from the same Zod schemas your handlers already validate against, so the contract and the runtime can’t disagree. Libraries like @asteasolutions/zod-to-openapi make this a generation step rather than a parallel document.

The CI staleness check is the part that makes this stick. Without it, the spec regenerates only when someone remembers, and you’re back to drift. With it, a schema change that doesn’t regenerate the doc fails the build.