| name | api-skill |
| description | Build or modify API endpoints in this repository by driving API contracts from src/api-serverless/openapi.yaml, regenerating generated models/routes, implementing thin handlers or legacy manual routes, and wiring validation/auth/timing correctly. Use when creating new API endpoints, changing API request or response models, adding generated x-6529-router operations, or updating API route behavior. |
API Development
Use this workflow for API contract, route, handler, and generated-model changes.
Workflow
- Update
src/api-serverless/openapi.yaml first for every public request or response shape change.
- Name new API schemas with an
Api prefix and use generated models from @/api/generated/models/....
- Prefer generated route wiring for new endpoints. Add
x-6529-router to the OpenAPI operation unless the generator cannot support the route shape or the user explicitly asks for manual routing:
x-6529-router:
enabled: true
auth: optional
cache: true
handler:
import: "@/api/some-feature/get-something.handler"
name: handleGetSomething
- After editing OpenAPI, run both commands from
src/api-serverless:
npm run restructure-openapi && npm run generate
npm run generate:openapi is equivalent when available.
- Treat
src/api-serverless/src/generated as generated-only; never edit it manually.
- Implement handler/service logic after generated types exist, then verify the contract matches OpenAPI.
Generated Routes
- Add or update the OpenAPI operation with
operationId, params, request/response schemas, and x-6529-router.
- It is OK if the handler file does not exist yet.
npm run generate writes the configured handler import/name into generated code; TypeScript fails until the handler is implemented.
- Implement the handler at the exact
x-6529-router.handler.import path and export the exact configured name.
- Import generated operation request/query/path/body/response types from
@/api/generated/routes/operations and generated models from @/api/generated/models/....
- Do not add duplicate manual
.routes.ts wiring or app wiring for generated endpoints. The generated router is already mounted in src/api-serverless/src/app.ts.
- If the generator rejects a needed route shape, extend
src/api-serverless/generate-openapi-routes.ts when that is in scope; otherwise use a manual route and call out why.
src/api-serverless/generate-openapi-routes.ts is the source of truth for generated-route constraints. Before relying on a detailed constraint below, verify it against that generator if the route shape or middleware behavior is unusual. Generated routes currently support:
- parameters with
in: path or in: query; all other parameter locations are rejected
- no
requestBody, which generates a request body type of never
requestBody.content.application/json.schema.$ref request bodies; other request body shapes are rejected
- responses from the
200 response only: application/json with a direct schema $ref, application/json with an array whose items are a $ref, or text/csv with schema type: string
Middleware Options
Use x-6529-router.auth for auth middleware and x-6529-router.cache for request caching:
x-6529-router:
enabled: true
auth: required
cache:
ttlSeconds: 900
authDependent: true
handler:
import: "@/api/some-feature/get-something.handler"
name: handleGetSomething
cache: true emits request caching.
cache.ttlSeconds configures the cache TTL.
cache.authDependent: true configures auth-aware cache behavior.
- Use
cache.methods when a generated route should cache non-GET methods.
Handler Rules
- For new generated endpoints, implement a thin handler file such as
src/api-serverless/src/<feature>/<operation>.handler.ts.
- Use generated operation types in the handler signature:
import { ApiSomething } from "@/api/generated/models/ApiSomething";
import { GetSomethingRequest } from "@/api/generated/routes/operations";
export async function handleGetSomething(
req: GetSomethingRequest
): Promise<ApiSomething> {
}
- Validate route input with Joi using
getValidatedByJoiOrThrow or getValidatedByJoi.
- Keep handlers thin: validate input, prepare request context, call services, and return generated response shapes.
- Never mark routes as cached unless explicitly instructed or an existing equivalent route is already cached for the same semantics.
- Manual
.routes.ts files are legacy/escape-hatch only.
Auth And Context
- Set
x-6529-router.auth: required when authentication is required; generated routing uses needsAuthenticatedUser().
- Set
x-6529-router.auth: optional when authentication is optional; generated routing uses maybeAuthenticatedUser().
- Use
getAuthenticationContext(req) after auth middleware when auth context is needed.
- Initialize
const timer = Timer.getFromRequest(req); when downstream work should be timed.
- Pass
timer or a RequestContext to downstream service/repository calls when those APIs expect it.
Validation