Skip to content

Instantly share code, notes, and snippets.

@daneuchar
Created February 6, 2026 08:02
Show Gist options
  • Select an option

  • Save daneuchar/6f53dc31eefdde3a8bc82c87b4da4cfa to your computer and use it in GitHub Desktop.

Select an option

Save daneuchar/6f53dc31eefdde3a8bc82c87b4da4cfa to your computer and use it in GitHub Desktop.
openapi
# Document Azure Function API Endpoints as Versioned OpenAPI Specs
## Purpose
Scan the entire TypeScript Azure Functions codebase, discover all HTTP-triggered
functions, extract their API metadata (route, method, request/response schemas,
auth level, parameters), group them by API version, and generate one OpenAPI 3.0
YAML file per version.
## Output Convention
| Version discovered | Output file |
| ------------------ | ------------------------- |
| `v1` | `docs/api/{module}-v1.yml` |
| `v2` | `docs/api/{module}-v2.yml` |
| `v3` | `docs/api/{module}-v3.yml` |
| *(no version)* | `docs/api/{module}-unversioned.yml` |
`{module}` is derived from the folder name, function app name, or logical
grouping found in the codebase. If the project is a single module, use the
project/package name.
---
## Step-by-Step Instructions
### 1. Discover All HTTP Trigger Functions
Scan every TypeScript file (`*.ts`) in the project for Azure Function HTTP
triggers. Look for **all** of the following patterns:
#### Programming Model v4 (Node.js v4)
```typescript
// Pattern A – app.http / app.get / app.post / app.put / app.patch / app.delete
import { app } from "@azure/functions";
app.http("functionName", {
methods: ["GET", "POST"],
authLevel: "anonymous",
route: "v1/users/{id}",
handler: handlerFn,
});
// Pattern B – app.route grouped
app.http("createUser", { methods: ["POST"], route: "v2/users", handler: ... });
app.http("getUser", { methods: ["GET"], route: "v2/users/{id}", handler: ... });
```
#### Programming Model v3 (function.json based)
```jsonc
// function.json
{
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
"methods": ["get"],
"route": "v1/products/{productId}",
"authLevel": "function"
},
{ "type": "http", "direction": "out" }
]
}
```
Also check `host.json` for a global `routePrefix` (commonly `"api"`).
### 2. Extract Metadata for Each Endpoint
For every discovered HTTP trigger, collect:
| Field | Source |
| ------------------ | --------------------------------------------------------------------------------------------- |
| **Route** | `route` property (combine with `routePrefix` from `host.json`) |
| **HTTP Methods** | `methods` array (default to all methods if omitted) |
| **Auth Level** | `authLevel` (`anonymous`, `function`, `admin`) |
| **Path Parameters**| Segments like `{id}`, `{productId}` in the route string |
| **Query Params** | Parsed from `request.query` / `req.query` usage in the handler |
| **Request Body** | TypeScript interfaces/types used for `await request.json()` or `req.body` |
| **Response Body** | Return type or `response.json(...)` calls — infer schema from TS types |
| **Status Codes** | Any explicit `status: 200`, `status: 404`, `new HttpResponse({ status: ... })` in the handler |
| **Description** | JSDoc `@description` / `@summary` on the handler, or inline comments |
| **Tags** | Derive from folder name, route prefix, or JSDoc `@tag` |
### 3. Determine API Version
Extract the version from the **route** string using these rules (in priority order):
1. **Explicit path segment**: `/v1/...`, `/v2/...`, `/v3/...` → version is `v1`, `v2`, `v3`
2. **Query parameter convention**: if handlers read `req.query.get("api-version")` → note each value
3. **Header convention**: if handlers read `x-api-version` header → note each value
4. **Folder structure**: `src/functions/v1/...`, `src/functions/v2/...`
5. **No version found**: classify as `unversioned`
### 4. Resolve TypeScript Types into JSON Schema
For every request/response type referenced by handlers:
- Follow `import` chains to find the interface or type alias definition.
- Convert TypeScript types to JSON Schema / OpenAPI `schema` objects:
| TypeScript | OpenAPI Schema |
| ----------------------- | ----------------------------------------------- |
| `string` | `{ type: "string" }` |
| `number` | `{ type: "number" }` |
| `boolean` | `{ type: "boolean" }` |
| `Date` | `{ type: "string", format: "date-time" }` |
| `string[]` | `{ type: "array", items: { type: "string" } }` |
| `Record<string, T>` | `{ type: "object", additionalProperties: <T> }` |
| `T \| null` | `{ oneOf: [<T>, { type: "null" }] }` |
| `T \| undefined` | mark property as **not required** |
| Enum | `{ type: "string", enum: [...] }` |
| Interface / Type alias | `$ref: "#/components/schemas/TypeName"` |
Place all shared schemas under `components.schemas` in the output YAML.
### 5. Generate the OpenAPI YAML Files
For each API version, produce a well-formed **OpenAPI 3.0.3** YAML document.
Use this skeleton:
```yaml
openapi: "3.0.3"
info:
title: "{Module Name} API – {Version}"
description: >
Auto-generated OpenAPI specification for the {module} Azure Functions
module, API version {version}.
version: "{version}"
contact:
name: "API Team"
servers:
- url: "https://{functionAppName}.azurewebsites.net/api"
description: "Azure Functions Host"
paths:
/{route}:
{method}:
operationId: "{functionName}"
summary: "{summary from JSDoc or inferred}"
description: "{description from JSDoc or inferred}"
tags:
- "{tag}"
security:
- functionKey: [] # if authLevel is "function"
- {} # if authLevel is "anonymous"
parameters:
- name: "{paramName}"
in: "path" # or "query"
required: true # path params are always required
schema:
type: "string"
requestBody: # only for POST/PUT/PATCH
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/{RequestType}"
responses:
"200":
description: "Successful response"
content:
application/json:
schema:
$ref: "#/components/schemas/{ResponseType}"
"400":
description: "Bad request"
"404":
description: "Not found"
"500":
description: "Internal server error"
components:
schemas:
{TypeName}:
type: "object"
required:
- "field1"
properties:
field1:
type: "string"
description: "{from JSDoc @property or inline comment}"
securitySchemes:
functionKey:
type: "apiKey"
in: "header"
name: "x-functions-key"
adminKey:
type: "apiKey"
in: "header"
name: "x-functions-key"
```
### 6. Quality Checks
Before writing each file, verify:
- [ ] Every `$ref` target exists in `components.schemas`.
- [ ] Path parameters in the route have matching `parameters` entries.
- [ ] `operationId` values are unique across the entire file.
- [ ] HTTP methods are lowercase (`get`, `post`, `put`, `delete`, `patch`).
- [ ] Required fields are listed for every object schema.
- [ ] No empty `paths` — if a version has no endpoints, skip the file.
- [ ] YAML is valid and parseable.
### 7. Generate a Summary Index
After all versioned files are created, produce a `docs/api/README.md` with:
```markdown
# API Documentation Index
> Auto-generated by Copilot from the Azure Functions TypeScript codebase.
## Modules & Versions
| Module | Version | File | Endpoints |
| ------------ | ------- | ----------------------------- | --------- |
| {module} | v1 | [{module}-v1.yml](./{module}-v1.yml) | 12 |
| {module} | v2 | [{module}-v2.yml](./{module}-v2.yml) | 8 |
## Quick Stats
- **Total endpoints**: {N}
- **Versions**: v1, v2
- **Auth levels used**: anonymous, function
## Changelog Hints
List any endpoints that exist in v1 but are missing in v2 (deprecated),
and any endpoints added in v2 that don't exist in v1 (new).
```
---
## Important Rules
1. **Do NOT hallucinate endpoints.** Only document functions you can find in the
source code with actual HTTP trigger bindings.
2. **Preserve the developer's naming.** Use the exact route strings, function
names, and type names from the code — do not rename them.
3. **Be thorough.** Walk every `src/` directory, every `functions/` folder, and
every `function.json`. Do not stop at the first file.
4. **Handle monorepos.** If the repo has multiple function apps (e.g.,
`apps/api-users`, `apps/api-orders`), treat each as a separate module.
5. **Idempotent.** Running this prompt again should produce the same output if
the source hasn't changed.
6. **Create the output directory** `docs/api/` if it doesn't exist.
7. **UTF-8 YAML** with `.yml` extension, 2-space indentation, no tabs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment