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.yamlfirst for every public request or response shape change. - Name new API schemas with an
Apiprefix and use generated models from@/api/generated/models/.... - Prefer generated route wiring for new endpoints. Add
x-6529-routerto 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 # optional | required | none cache: true # optional; emits cacheRequest() 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 generatenpm run generate:openapiis equivalent when available. - Treat
src/api-serverless/src/generatedas 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, andx-6529-router. - It is OK if the handler file does not exist yet.
npm run generatewrites 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.importpath and export the exact configuredname. - Import generated operation request/query/path/body/response types from
@/api/generated/routes/operationsand generated models from@/api/generated/models/.... - Do not add duplicate manual
.routes.tswiring or app wiring for generated endpoints. The generated router is already mounted insrc/api-serverless/src/app.ts. - If the generator rejects a needed route shape, extend
src/api-serverless/generate-openapi-routes.tswhen that is in scope; otherwise use a manual route and call out why.
Generated routes currently support:
- parameters with
in: pathorin: query; all other parameter locations are rejected - no
requestBody, which generates a request body type ofnever requestBody.content.application/json.schema.$refrequest bodies; other request body shapes are rejected- responses from the
200response only:application/jsonwith a direct schema$ref,application/jsonwith an array whoseitemsare a$ref, ortext/csvwith schematype: 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: trueemitscacheRequest().cache.ttlSecondsemitscacheRequest({ ttl: Time.seconds(value) }).cache.authDependent: trueincludes the authenticated/anonymous identity in the cache key.- Use
cache.methodswhen 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 input, call service, return generated response shape } - Validate route input with Joi using
getValidatedByJoiOrThroworgetValidatedByJoi. - 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.tsfiles are legacy/escape-hatch only.
Auth And Context
- Set
x-6529-router.auth: requiredwhen authentication is required; generated routing usesneedsAuthenticatedUser(). - Set
x-6529-router.auth: optionalwhen authentication is optional; generated routing usesmaybeAuthenticatedUser(). - Use
getAuthenticationContext(req)after auth middleware when auth context is needed. - Initialize
const timer = Timer.getFromRequest(req);when downstream work should be timed. - Pass
timeror aRequestContextto downstream service/repository calls when those APIs expect it.
Validation
- Updated
src/api-serverless/openapi.yamlfirst. - All new schemas/models in OpenAPI start with
Api. - Added
x-6529-router.enabled: truefor new generated endpoints. - Added
x-6529-router.cacheonly when request caching is required. - Declared the final handler import/name in
x-6529-router.handler. - Ran
cd src/api-serverless && npm run restructure-openapi && npm run generate. - Did not manually edit
src/api-serverless/src/generated/*. - Used generated operation types and generated models.
- Added Joi validation.
- Set auth correctly.
- Used
getAuthenticationContext(req)where needed. - Kept handler logic light and service-driven.
- Avoided duplicate manual route/app wiring for generated endpoints.
- Verified route behavior matches
openapi.yaml.