openapi-endpoints
npx skills add https://github.com/timelessco/recollect --skill openapi-endpoints
Agent 安装分布
Skill 文档
OpenAPI Endpoint Documentation
The spec is generated in two passes: (1) a filesystem scanner auto-infers schemas from handler factories, (2) a merge script overlays human-authored metadata from supplement files. The full human-readable guide is at docs/OPENAPI_GUIDE.md.
Quick Start
New App Router endpoint
- Create
src/app/api/<path>/route.tsusing a handler factory - Create
src/app/api/<path>/schema.tswithInputSchema+OutputSchema - Create
src/lib/openapi/endpoints/<domain>/<endpoint-name>.ts(supplement) - Export supplement from
src/lib/openapi/endpoints/<domain>/index.ts - Run
npx tsx scripts/generate-openapi.ts - Verify at
http://localhost:3000/api-docs
Update existing endpoint
- Modify schema in
schema.tsâ auto-captured by scanner - Update supplement metadata/examples in
src/lib/openapi/endpoints/<domain>/ - Run
npx tsx scripts/generate-openapi.ts
New edge function endpoint
- Create registration function in
src/lib/openapi/endpoints/<domain>/edge-process-imports.ts - Use
registry.registerPath()with rawSchemaObject(not Zod) - Import and call from
scripts/generate-openapi.ts - Run
npx tsx scripts/generate-openapi.ts - Verify at
http://localhost:3000/api-docs
For edge function details, see reference.md.
New App Router Endpoint
Step 1: Create route handler
Use one of the 4 handler factories from src/lib/api-helpers/create-handler.ts:
createGetApiHandlerWithAuthâ GET with authcreatePostApiHandlerWithAuthâ POST with authcreateGetApiHandlerâ GET without authcreatePostApiHandlerâ POST without auth
// src/app/api/<domain>/<endpoint>/route.ts
import { createGetApiHandlerWithAuth } from "@/lib/api-helpers/create-handler";
import { MyInputSchema, MyOutputSchema } from "./schema";
const ROUTE = "domain-endpoint-name";
export const GET = createGetApiHandlerWithAuth({
route: ROUTE,
inputSchema: MyInputSchema,
outputSchema: MyOutputSchema,
handler: async ({ data, supabase, user, route }) => {
// business logic
return { result: "value" };
},
});
Step 2: Create schema file
Colocate Zod schemas next to route.ts:
// src/app/api/<domain>/<endpoint>/schema.ts
import { z } from "zod";
export const MyInputSchema = z.object({
url: z.string(),
});
export const MyOutputSchema = z.object({
result: z.string(),
});
Step 3: Create supplement file
Supplements provide metadata the scanner can’t infer: tags, summary, description, examples.
// src/lib/openapi/endpoints/<domain>/<endpoint-name>.ts
import { type EndpointSupplement } from "@/lib/openapi/supplement-types";
import { bearerAuth } from "@/lib/openapi/registry";
export const myEndpointSupplement = {
path: "/<domain>/<endpoint-name>",
method: "get",
tags: ["<Domain>"],
summary: "Short description for Scalar heading",
description: "Detailed explanation of what the endpoint does.",
security: [{ [bearerAuth.name]: [] }, {}],
responseExample: {
data: { result: "value" },
error: null,
},
} satisfies EndpointSupplement;
Rules:
pathis relative to/api(e.g.,/bookmarks/check-urlnot/api/bookmarks/check-url)methodis lowercase:"get"or"post"- Tags are capitalized:
"Bookmarks","Categories","Twitter" - Security
[{ [bearerAuth.name]: [] }, {}]â empty{}means cookie auth also accepted - Export name:
<camelCaseName>Supplement
Step 4: Export from barrel
// src/lib/openapi/endpoints/<domain>/index.ts
export { myEndpointSupplement } from "./<endpoint-name>";
Step 5: Regenerate and verify
npx tsx scripts/generate-openapi.ts
Open http://localhost:3000/api-docs and verify the endpoint appears.
Updating an Existing Endpoint
Schema changes
Modify the Zod schema in schema.ts â the scanner picks it up automatically. The GitHub Actions changelog workflow runs oasdiff on every push to dev and appends changes to docs/API_CHANGELOG.md.
Updating supplement metadata
Edit the supplement file directly: summary, description, tags, additionalResponses.
Adding examples
Single example (simple endpoints):
responseExample: {
data: { id: 1, name: "Example" },
error: null,
},
Named examples (multiple scenarios â creates a dropdown in Scalar):
responseExamples: {
"happy-path": {
summary: "Successful response",
description: "Returns the created resource.",
value: { data: { id: 1 }, error: null },
},
"duplicate-detected": {
summary: "Duplicate skipped",
description: "URL already bookmarked.",
value: { data: { inserted: 0, skipped: 1 }, error: null },
},
},
400 error examples:
response400Examples: {
"empty-array": {
summary: "Empty bookmarks array",
description: "Fails when bookmarks array has no elements.",
value: { data: null, error: "bookmarks: Array must contain at least 1 element(s)" },
},
},
additionalResponses: {
400: { description: "Invalid request body or bookmark data" },
},
When the supplement file exceeds 250 lines, extract examples to <endpoint-name>-examples.ts.
Parameter examples (GET query param dropdown in Scalar):
parameterExamples: {
email: {
"valid-user": {
summary: "Valid user email",
description: "Returns user data for this email.",
value: "user@example.com",
},
"unknown-email": {
summary: "Nonexistent email",
description: "Returns null/empty result.",
value: "nobody@example.com",
},
},
},
Outer key = parameter name (must match name in the generated spec). Inner map = standard named examples. Creates a dropdown per query param in Scalar’s “Try It” panel.
Supplement Reference
EndpointSupplement fields
| Field | Type | Purpose |
|---|---|---|
path |
string |
Route path relative to /api (required) |
method |
string |
"get" or "post" lowercase (required) |
tags |
string[] |
Scalar sidebar grouping |
summary |
string |
Short heading (one line) |
description |
string |
Detailed explanation (supports markdown) |
security |
Array<Record<string, string[]>> |
Auth requirements |
requestExample |
Record<string, unknown> |
Single request body example |
requestExamples |
Named examples object | Multiple request examples |
responseExample |
Record<string, unknown> |
Single 200 response example |
responseExamples |
Named examples object | Multiple 200 response examples |
response400Example |
Record<string, unknown> |
Single 400 error example |
response400Examples |
Named examples object | Multiple 400 error examples |
additionalResponses |
Record<number, { description }> |
Extra response codes (400, 403, 404) |
parameterExamples |
Record<string, NamedExamples> |
Per-param named examples (GET dropdown) |
Naming conventions
- Export name:
<camelCaseName>Supplement(e.g.,checkUrlSupplement) - Named example keys: kebab-case (
"single-tweet","validation-error") - Example files:
<endpoint-name>-examples.ts(e.g.,sync-examples.ts) - Tags: capitalized (
"Bookmarks","iPhone") - All named examples require both
summaryanddescription - Happy paths first, then validation errors
Response components
The scanner registers 3 $ref response components for every endpoint:
ValidationError(400) â{ data: null, error: string }Unauthorized(401) â{ data: null, error: "Not authenticated" }InternalError(500) â{ data: null, error: "Failed to process request" }
additionalResponses overrides the 400 description while preserving the schema.
Agent Prompt Templates
Create supplement for a new endpoint
I just created a new App Router endpoint at
src/app/api/<path>/route.tswith schema atsrc/app/api/<path>/schema.ts. Create the OpenAPI supplement file, export it from the barrel, regenerate the spec, and verify at/api-docs.Domain:
<bookmarks|categories|tags|twitter|instagram|raindrop|profiles|iphone>
Update examples for an existing endpoint
Update the OpenAPI examples for the
<endpoint-name>endpoint. Read the current supplement atsrc/lib/openapi/endpoints/<domain>/<endpoint-name>.ts, add named examples for these scenarios: [describe scenarios]. Regenerate the spec and verify.
Add 400 error examples
Add
response400Examplesto the<endpoint-name>supplement. Include examples for: [list validation errors]. Also addadditionalResponses: { 400: { description: "<custom description>" } }. Regenerate and verify.
Add parameter examples for a GET endpoint
Add
parameterExamplesto the<endpoint-name>supplement for each query parameter. Include examples for: [list test scenarios per param]. Regenerate the spec and verify the dropdown appears in Scalar’s “Try It” panel.
Document a new edge function
I created a new Supabase Edge Function at
supabase/functions/<name>/. Register it in the OpenAPI spec following the pattern insrc/lib/openapi/endpoints/instagram/edge-process-imports.ts. UseserviceRoleAuth,edgeFunctionServers, and rawSchemaObject. Wire it intoscripts/generate-openapi.ts.
Troubleshooting
Supplement not appearing in spec
- Is the supplement exported from the domain’s
index.tsbarrel? - Does
pathmatch the route path exactly (relative to/api, no trailing slash)? - Does
methodmatch the exported handler ("get"forGET,"post"forPOST)? - Check console output â
mergeSupplementsprints warnings for unmatched supplements
400 examples not showing
- Does the supplement have
additionalResponses: { 400: { description: "..." } }? - Are named examples using
response400Examples(notresponseExamples)? - The merge script initializes 400 content automatically when examples are provided
Common mistakes
- Using
/api/bookmarks/check-urlinstead of/bookmarks/check-urlfor the path - Forgetting
as conston example data objects in-examples.tsfiles - Using
responseExample(singular) when you needresponseExamples(named/plural) - Missing
summaryordescriptionon named examples â both are required
For edge function patterns and complex examples, see reference.md.