API Reference Generation
Build From Scratch: Mintlify-like AI-driven Documentation Generator CLI - Part 021
Build a production-grade API reference generator from OpenAPI, discovered routes, examples, schemas, auth models, errors, and navigation contracts.
Part 021 — API Reference Generation
Pada part sebelumnya kita sudah membangun MDX authoring engine dan example-aware documentation generation. Sekarang kita masuk ke salah satu output paling bernilai dari sistem ini: API reference generation.
Target kita bukan sekadar membuat halaman endpoint dari openapi.yaml.
Targetnya lebih tajam:
Kita ingin membangun generator yang mampu membaca kontrak API, source code, tests, examples, auth model, error model, dan konfigurasi publishing, lalu menghasilkan API reference yang benar, navigable, source-grounded, reviewable, dan tidak cepat basi.
API reference yang buruk biasanya punya pola yang sama:
- endpoint ada, tetapi tidak ada konteks penggunaan;
- schema lengkap, tetapi tidak membantu developer memahami flow;
- auth disebut sekilas, tetapi tidak jelas kapan token diperlukan;
- error response tidak lengkap;
- examples tidak runnable;
- OpenAPI berubah, tetapi docs tidak ikut berubah;
- docs menjelaskan behavior yang tidak lagi ada di kode;
- navigation berantakan karena endpoint hanya diurutkan mentah berdasarkan path;
- generated docs menimpa penjelasan manual yang sebenarnya penting.
Sistem yang kita bangun harus menghindari semua itu.
1. Mental Model: API Reference Is a Contract View, Not a Blog Post
API reference berbeda dari guide.
Guide menjawab:
“Bagaimana saya mencapai satu tujuan?”
API reference menjawab:
“Apa persisnya surface area API ini, bagaimana request/response-nya, constraint-nya, error-nya, dan bagaimana cara memanggilnya dengan benar?”
Namun, API reference juga tidak boleh hanya menjadi dump schema.
API reference production-grade adalah gabungan dari beberapa lapisan:
Setiap halaman endpoint harus menjawab:
- endpoint ini untuk apa;
- siapa yang boleh memanggil;
- method dan path;
- path/query/header/body parameters;
- request body;
- response body;
- error response;
- contoh request yang valid;
- contoh response yang realistis;
- constraints, idempotency, pagination, rate limit, atau side effect;
- source/provenance dari klaim penting;
- relasi dengan guide atau workflow lain.
2. Input Artifact yang Dibutuhkan
Generator API reference tidak boleh langsung membaca file mentah secara acak.
Ia harus membaca artifact yang sudah kita desain sebelumnya:
.aidocs/
scans/
scan.v1.json
maps/
repo-map.v1.json
symbols/
symbols.v1.json
contracts/
contracts.v1.json
examples/
examples.v1.json
plans/
doc-plan.v1.json
pages/
page-specs/
api-reference-users-get.v1.json
Minimal input:
| Artifact | Peran |
|---|---|
contracts.v1.json | daftar API contract, endpoint, schema, auth, examples dari OpenAPI/route discovery |
examples.v1.json | usage example dari test, fixture, README, SDK sample |
symbols.v1.json | hubungan endpoint dengan handler/function/class |
repo-map.v1.json | konteks project, framework, API roots, docs root |
doc-plan.v1.json | daftar halaman API reference yang harus dibuat |
page-spec.v1.json | contract halaman tertentu |
| existing MDX | human notes yang perlu dipertahankan |
Kenapa perlu sebanyak ini?
Karena OpenAPI saja sering tidak cukup.
OpenAPI bisa mendeskripsikan surface. Tetapi source code dan tests sering menyimpan real behavior:
- status code tambahan;
- auth middleware;
- validation rule;
- idempotency key;
- rate-limit header;
- pagination convention;
- default value;
- error envelope;
- example payload yang benar-benar dipakai.
3. Baseline Faktual: OpenAPI dan Mintlify-like Output
OpenAPI Specification mendefinisikan interface description yang language-agnostic untuk HTTP API, sehingga manusia dan komputer dapat memahami capability service tanpa membaca source code atau traffic langsung. Pada 2026, versi OAS 3.2.0 sudah tersedia, tetapi banyak tool docs masih memprioritaskan 3.0 dan 3.1, sehingga generator kita harus version-aware dan tidak mengasumsikan semua renderer mendukung fitur terbaru.
Mintlify sendiri mendokumentasikan bahwa API pages dapat di-auto-populate dari dokumen OpenAPI dengan menambahkan field openapi pada elemen navigation di docs.json, dan dokumentasi Mintlify saat ini menyebut dukungan OpenAPI 3.0 dan 3.1 untuk setup tersebut.
Rujukan:
- OpenAPI Specification v3.2.0: https://spec.openapis.org/oas/v3.2.0.html
- OpenAPI Initiative announcement for v3.2: https://www.openapis.org/blog/2025/09/23/announcing-openapi-v3-2
- Mintlify OpenAPI setup: https://www.mintlify.com/docs/api-playground/openapi-setup
- Mintlify navigation docs: https://www.mintlify.com/docs/organize/navigation
Implikasi untuk sistem kita:
- internal parser boleh mendukung OAS 3.0, 3.1, dan 3.2;
- publisher adapter harus tahu target renderer mendukung versi apa;
- jika target Mintlify-like adapter hanya kompatibel dengan subset tertentu, generator harus melakukan downgrade/normalization atau memberi diagnostic;
- docs tidak boleh diam-diam publish schema yang tidak dapat dirender.
4. API Reference Generator Pipeline
Pipeline utama:
Lebih detail:
5. Contract Normalization
API sources bisa datang dari banyak tempat:
openapi.yaml;openapi.json;- generated OpenAPI dari framework;
- route annotations;
- controller definitions;
- GraphQL schema;
- AsyncAPI;
- hand-written route table;
- CLI command descriptors;
- gRPC/protobuf HTTP transcoding.
Pada part ini kita fokus pada HTTP API reference berbasis OpenAPI + discovered route.
Kita perlu normalize semua endpoint ke model internal.
Contoh model:
type HttpApiEndpoint = {
id: string
source: ContractSource
method: HttpMethod
path: string
operationId?: string
summary?: string
description?: string
tags: string[]
stability?: "experimental" | "beta" | "stable" | "deprecated"
auth: AuthRequirement[]
parameters: ApiParameter[]
requestBody?: ApiRequestBody
responses: ApiResponse[]
errors: ApiError[]
examples: ApiExampleRef[]
implementationRefs: SourceRef[]
testRefs: SourceRef[]
provenance: Provenance[]
diagnostics: Diagnostic[]
}
Endpoint ID harus stable.
Jangan memakai index array.
Gunakan:
http:<method>:<normalized-path>
Contoh:
http:get:/v1/users/{userId}
http:post:/v1/orders
http:delete:/v1/sessions/{sessionId}
Jika ada version:
http:v1:get:/users/{userId}
Tetapi hati-hati: version bisa sudah ada di path /v1. Jangan menggandakan tanpa rule jelas.
6. OpenAPI Parser Design
OpenAPI memiliki struktur besar:
openapi: 3.1.0
info:
title: Example API
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/users/{userId}:
get:
operationId: getUser
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
components:
schemas:
User:
type: object
Generator harus memahami minimal:
info;servers;paths;- methods;
operationId;tags;summary;description;parameters;requestBody;responses;security;components.securitySchemes;components.schemas;$ref.
Parser sebaiknya dipisah menjadi dua tahap:
Jangan membuat generator MDX langsung membaca YAML raw.
Kenapa?
Karena $ref, inherited security, global parameters, reusable responses, dan component schemas membuat data mentah tidak nyaman untuk docs generation.
7. $ref Resolution Strategy
$ref bisa menunjuk ke:
- local component;
- local path;
- external file;
- remote URL;
- JSON Pointer.
Contoh:
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/User"
Model internal harus menyimpan dua bentuk:
- resolved view untuk generation;
- original ref untuk provenance.
type ResolvedSchema = {
ref?: string
name?: string
schema: JsonSchemaLike
sourceRef: SourceRef
}
Kenapa original ref penting?
Karena saat docs berubah, kita ingin tahu bahwa response User berasal dari #/components/schemas/User, bukan dari LLM.
8. Schema Rendering Policy
Schema rendering adalah tempat banyak docs menjadi tidak terbaca.
Jangan dump JSON Schema mentah tanpa hierarki.
Gunakan policy:
- tampilkan nama schema;
- tampilkan deskripsi singkat;
- tampilkan required fields;
- tampilkan field table;
- tampilkan nested object secukupnya;
- tampilkan example;
- link ke schema reference jika terlalu panjang.
Contoh field table:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique user identifier |
email | string | yes | User email address |
createdAt | string(date-time) | yes | Creation timestamp |
Internal renderer:
type SchemaView = {
title: string
description?: string
required: string[]
fields: SchemaFieldView[]
examples: unknown[]
nestedSchemas: SchemaView[]
sourceRefs: SourceRef[]
}
Rule penting:
- jangan flatten object terlalu agresif;
- jangan expand recursive schema tanpa depth limit;
- jangan mengarang description field jika tidak ada sumber;
- jika description kosong, tulis type dan constraint saja;
- jika enum ada, tampilkan enum values;
- jika format ada, tampilkan format;
- jika nullable ada, tampilkan;
- jika deprecated ada, tampilkan.
9. Endpoint Page Structure
Halaman endpoint yang baik punya struktur stabil.
Contoh MDX target:
---
title: Get User
description: Retrieve a user by ID.
api: GET /v1/users/{userId}
---
# Get User
Retrieve a user by ID.
## Endpoint
`GET /v1/users/{userId}`
## Authentication
Requires a bearer token with `users:read`.
## Path Parameters
| Name | Type | Required | Description |
|---|---:|---:|---|
| `userId` | `string` | yes | User identifier. |
## Query Parameters
No query parameters.
## Request Body
This endpoint does not accept a request body.
## Responses
### `200 OK`
Returns the user.
### `404 Not Found`
The user does not exist.
## Example Request
```bash
curl -X GET "https://api.example.com/v1/users/usr_123" \
-H "Authorization: Bearer $TOKEN"
Example Response
{
"id": "usr_123",
"email": "alice@example.com"
}
Namun untuk sistem kita, MDX page juga harus menyimpan provenance.
Contoh metadata internal di komentar:
```mdx
{/* aidocs:source contract=openapi.yaml#/paths/~1v1~1users~1{userId}/get */}
{/* aidocs:generated section=responses hash=abc123 */}
10. Page Types untuk API Reference
Tidak semua API docs harus satu halaman per endpoint.
Kita butuh beberapa jenis halaman:
| Page Type | Tujuan |
|---|---|
| API overview | menjelaskan API product secara umum |
| Authentication | menjelaskan auth schemes |
| Errors | menjelaskan error envelope |
| Pagination | menjelaskan pagination convention |
| Rate limits | menjelaskan quota dan headers |
| Webhooks/events | menjelaskan async callbacks |
| Endpoint group landing | landing page per resource/domain |
| Endpoint page | detail satu operation |
| Schema reference | model/schema reusable |
| Changelog/versioning | breaking changes, deprecation |
Banyak generator hanya membuat endpoint pages. Itu tidak cukup.
Developer sering butuh halaman konseptual:
- “How authentication works”
- “How pagination works”
- “How errors are structured”
- “How idempotency works”
- “How webhooks are signed”
Kalau semua dimasukkan ke tiap endpoint, docs menjadi repetitive.
11. Grouping Endpoints
Endpoint grouping tidak boleh hanya berdasarkan tag mentah.
OpenAPI tags berguna, tetapi sering tidak konsisten.
Gunakan kombinasi:
- explicit OpenAPI tags;
- path prefix;
- controller/package ownership;
- domain nouns;
- navigation config override;
- operationId pattern.
Contoh:
GET /v1/users
POST /v1/users
GET /v1/users/{id}
PATCH /v1/users/{id}
DELETE /v1/users/{id}
Group:
Users
Tapi contoh ini:
POST /v1/auth/login
POST /v1/auth/logout
POST /v1/sessions
DELETE /v1/sessions/{id}
Bisa jadi group:
Authentication
atau:
Sessions
Tergantung product language. Generator harus bisa menerima override.
Config:
api:
groups:
- name: Authentication
match:
paths:
- /v1/auth/**
- /v1/sessions/**
- name: Users
match:
tags:
- Users
12. Operation Naming
Nama halaman endpoint harus human-friendly.
Sumber naming:
summary;operationId;- method + path;
- handler name;
- generated verb+noun.
Contoh:
| Raw | Generated Title |
|---|---|
getUser | Get User |
POST /v1/users | Create User |
PATCH /v1/orders/{orderId}/cancel | Cancel Order |
DELETE /v1/sessions/{sessionId} | Delete Session |
Naming rule sederhana:
GET collection -> List <Resources>
GET item -> Get <Resource>
POST collection -> Create <Resource>
PUT item -> Replace <Resource>
PATCH item -> Update <Resource>
DELETE item -> Delete <Resource>
POST action -> <Action Verb> <Resource>
Tapi jangan paksa jika summary sudah jelas.
13. Authentication Section Generation
Auth harus dijelaskan di dua level:
- global auth docs;
- endpoint-level requirement.
OpenAPI security scheme bisa berupa:
- HTTP bearer;
- HTTP basic;
- API key;
- OAuth2;
- OpenID Connect;
- mutual TLS;
- custom headers.
Model internal:
type AuthRequirement = {
schemeId: string
type: "bearer" | "basic" | "apiKey" | "oauth2" | "openid" | "mutualTLS" | "custom"
scopes: string[]
required: boolean
sourceRefs: SourceRef[]
}
Endpoint page harus menjawab:
- apakah auth required;
- scheme apa;
- scope/permission apa;
- header apa;
- apakah endpoint public;
- error apa jika auth gagal.
Contoh:
## Authentication
Requires a bearer token with the `users:read` scope.
Send the token in the `Authorization` header:
```http
Authorization: Bearer <token>
Jika tidak ada sumber untuk scope:
```mdx
## Authentication
Requires bearer authentication.
The source contract does not specify a scope.
Itu lebih jujur daripada mengarang users:read.
14. Error Model Generation
Error docs sering paling lemah.
Endpoint tidak cukup hanya menampilkan:
"400":
description: Bad Request
Kita perlu error model:
type ApiError = {
status: number
code?: string
title?: string
description?: string
schemaRef?: string
example?: unknown
sourceRefs: SourceRef[]
}
Sources:
- OpenAPI responses;
- error classes;
- exception handlers;
- tests;
- fixtures;
- README docs;
- handler code.
Output minimum:
## Errors
| Status | Code | Meaning |
|---:|---|---|
| `400` | `invalid_request` | Request validation failed. |
| `401` | `unauthorized` | Missing or invalid authentication. |
| `404` | `user_not_found` | No user exists for the given ID. |
Jika error envelope global:
{
"error": {
"code": "user_not_found",
"message": "User not found",
"requestId": "req_123"
}
}
Maka buat halaman:
api/errors.mdx
dan endpoint pages cukup link ke sana.
15. Pagination, Filtering, Sorting, and Idempotency
Banyak API docs gagal karena hanya mendeskripsikan parameter, tetapi tidak menjelaskan convention.
Cari convention dari:
- repeated query parameters;
- schema names;
- tests;
- common parameter refs;
- middleware;
- helper functions.
Contoh detection:
parameters:
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/Limit"
atau source:
const page = parseInt(req.query.page ?? "1")
const limit = Math.min(parseInt(req.query.limit ?? "20"), 100)
Jika ditemukan:
- buat konsep
Pagination; - hubungkan endpoint list ke konsep itu;
- tampilkan ringkasan di endpoint page;
- jangan duplikasi semua detail.
Endpoint page:
## Pagination
This endpoint uses page-based pagination.
See [Pagination](/api/pagination) for common response fields and limits.
Halaman pagination:
# Pagination
List endpoints use `page` and `limit`.
The default `limit` is `20`.
The maximum `limit` is `100`.
Klaim default/max harus punya source. Jika hanya inferensi dari code, tandai sebagai inferred.
16. Request Example Generation
Example request harus berasal dari sumber nyata.
Priority:
- explicit OpenAPI example;
- test fixture;
- integration test;
- SDK example;
- generated from schema with clear marker;
- LLM-generated fallback only if allowed.
Policy:
examples:
request:
preferredSources:
- openapi
- tests
- fixtures
allowSynthetic: false
Jika fallback synthetic diizinkan, docs harus jelas:
{/* aidocs:example synthetic=true reason="no source example found" */}
Tapi default production-grade harus:
No verified request example found.
Lebih baik kosong daripada menipu.
17. Response Example Generation
Response example lebih berbahaya daripada request example.
Kalau kita mengarang response, developer bisa membangun client berdasarkan field palsu.
Gunakan:
- OpenAPI response example;
- captured fixture;
- contract test snapshot;
- schema-derived minimal example dengan marker;
- no example.
Schema-derived example harus deterministic.
Contoh generator:
function exampleForSchema(schema: Schema): unknown {
if (schema.example !== undefined) return schema.example
if (schema.default !== undefined) return schema.default
if (schema.enum?.length) return schema.enum[0]
if (schema.type === "string") return "string"
if (schema.type === "integer") return 0
if (schema.type === "boolean") return true
if (schema.type === "array") return [exampleForSchema(schema.items)]
if (schema.type === "object") return exampleObject(schema)
return null
}
Tapi output harus diberi label:
{/* aidocs:example generatedFromSchema=true */}
18. API Reference as MDX + Structured Metadata
Kita ingin output bisa dibaca manusia dan diproses mesin.
File MDX:
docs/api/users/get-user.mdx
Metadata:
------
title: Get User
description: Retrieve a user by ID.
api:
method: GET
path: /v1/users/{userId}
operationId: getUser
source: openapi.yaml#/paths/~1v1~1users~1{userId}/get
generated:
by: aidocs
artifact: api-reference-page.v1
endpointId: http:get:/v1/users/{userId}
contentHash: sha256:...
---
MDX content:
# Get User
Retrieve a user by ID.
<Endpoint method="GET" path="/v1/users/{userId}" />
## Authentication
Requires bearer authentication.
## Parameters
...
Jika target renderer tidak support custom component, renderer adapter bisa menurunkan ke Markdown biasa.
19. Mintlify-like Navigation Output
Mintlify memakai docs.json untuk konfigurasi navigation.
Kita tidak harus mengunci semua output hanya untuk Mintlify, tetapi adapter Mintlify-like harus bisa menghasilkan:
{
"navigation": {
"tabs": [
{
"tab": "API Reference",
"groups": [
{
"group": "Users",
"pages": [
"api/users/list-users",
"api/users/create-user",
"api/users/get-user"
]
}
]
}
]
}
}
Untuk auto-populated OpenAPI pages, Mintlify mendokumentasikan penggunaan field openapi pada navigation element. Tetapi karena kita membangun generator sendiri, ada dua mode:
Mode A — Delegate to renderer
{
"group": "API Reference",
"openapi": "openapi.yaml"
}
Kelebihan:
- simple;
- renderer menangani endpoint pages;
- lebih dekat dengan Mintlify native behavior.
Kekurangan:
- sulit memasukkan provenance per section;
- sulit preserve human edits per endpoint;
- sulit enforce custom anti-hallucination policy;
- tergantung capability renderer.
Mode B — Generate explicit MDX pages
{
"group": "Users",
"pages": [
"api/users/list-users",
"api/users/get-user"
]
}
Kelebihan:
- full control;
- bisa source-grounded;
- bisa human review;
- bisa custom examples;
- bisa merge notes dan generated sections.
Kekurangan:
- lebih banyak file;
- lebih banyak logic generator;
- harus maintain compatibility sendiri.
Untuk seri ini, kita gunakan Mode B sebagai primary, dan Mode A sebagai optional adapter.
20. Endpoint Page Generation Algorithm
Pseudocode:
function generateApiReference(project: ProjectArtifacts): ApiReferenceOutput {
const contracts = loadContracts(project)
const examples = loadExamples(project)
const symbols = loadSymbols(project)
const plan = loadDocPlan(project)
const normalized = normalizeContracts(contracts)
const enriched = normalized.endpoints.map(endpoint =>
enrichEndpoint(endpoint, { examples, symbols, contracts })
)
const groups = groupEndpoints(enriched, project.config.api.grouping)
const pages = []
for (const group of groups) {
pages.push(generateGroupLandingPage(group))
for (const endpoint of group.endpoints) {
const pageSpec = buildEndpointPageSpec(endpoint, group)
const mdx = renderEndpointPage(pageSpec)
const report = verifyEndpointPage(mdx, pageSpec)
pages.push({ pageSpec, mdx, report })
}
}
const nav = generateApiNavigation(groups, pages)
return { pages, nav, reports }
}
Perhatikan urutannya:
- normalize;
- enrich;
- group;
- build page spec;
- render;
- verify;
- navigation.
Jangan generate dulu lalu coba rapikan navigation belakangan. Itu membuat struktur docs tidak stabil.
21. Building api-reference.v1.json
Selain MDX, buat artifact intermediate:
{
"schemaVersion": "api-reference.v1",
"generatedAt": "2026-07-04T00:00:00Z",
"sourceContracts": [
"openapi.yaml"
],
"groups": [
{
"id": "users",
"title": "Users",
"description": "Manage users.",
"endpoints": [
"http:get:/v1/users",
"http:post:/v1/users",
"http:get:/v1/users/{userId}"
],
"page": "api/users/overview"
}
],
"pages": [
{
"id": "api.users.get-user",
"path": "api/users/get-user.mdx",
"endpointId": "http:get:/v1/users/{userId}",
"operationId": "getUser",
"sourceRefs": [
"openapi.yaml#/paths/~1v1~1users~1{userId}/get"
],
"contentHash": "sha256:..."
}
],
"diagnostics": []
}
Kenapa perlu?
- preview UI bisa membaca artifact tanpa parsing MDX;
- verifier bisa diff API reference antar build;
- CI bisa mendeteksi endpoint ditambahkan/dihapus;
- KM sink bisa membuat note untuk endpoint;
- navigation generator bisa idempotent.
22. Versioning Strategy
API reference harus mendukung versioning.
Tiga pola umum:
Path versioning
/v1/users
/v2/users
Header versioning
Accept: application/vnd.example.v2+json
Docs versioning
docs/v1/api/users
docs/v2/api/users
Generator harus memisahkan:
- API version;
- docs version;
- product version;
- package version.
Jangan campur.
Config:
api:
versions:
- id: v1
source: openapi-v1.yaml
output: api/v1
- id: v2
source: openapi-v2.yaml
output: api/v2
Endpoint ID:
http:v1:get:/users
http:v2:get:/users
Navigation:
{
"tabs": [
{
"tab": "API v1",
"groups": []
},
{
"tab": "API v2",
"groups": []
}
]
}
23. Deprecation Handling
OpenAPI operation can mark operation as deprecated.
Example:
get:
deprecated: true
Output:
<Warning>
This endpoint is deprecated.
</Warning>
But production-grade docs should also answer:
- since when deprecated;
- replacement endpoint;
- removal date;
- migration guide;
- behavior differences.
Sources might include:
- OpenAPI extension fields;
- changelog;
- release notes;
- comments;
- manually maintained docs.
Custom extension:
x-aidocs-deprecation:
since: "2026-06-01"
replacement: "GET /v2/users/{userId}"
removal: "2027-01-01"
migrationGuide: "/api/migration/v1-to-v2"
Do not invent removal date.
24. Extension Fields
OpenAPI supports extension fields that begin with x-.
We can use them carefully.
Examples:
x-aidocs-group: Users
x-aidocs-page-title: Retrieve User
x-aidocs-hide: false
x-aidocs-stability: beta
x-aidocs-owner: team-identity
x-aidocs-examples:
- tests/users/get-user.test.ts
Rules:
- extension fields must be optional;
- generator should work without them;
- extension fields should override heuristics;
- invalid extension field should produce diagnostic;
- never pollute external contract unless team agrees.
25. API Reference Verification
Verifier harus mengecek:
| Check | Purpose |
|---|---|
| endpoint exists | generated page maps to real contract |
| method/path correct | prevent wrong endpoint |
| params complete | prevent missing required params |
| request body matches | prevent fake payload |
| responses complete | prevent missing status codes |
| auth matches | prevent public/private confusion |
| examples valid | prevent broken snippets |
| links valid | prevent broken navigation |
| frontmatter valid | prevent renderer failure |
| stale hash | detect drift |
Example verification result:
{
"page": "api/users/get-user.mdx",
"endpointId": "http:get:/v1/users/{userId}",
"status": "failed",
"errors": [
{
"code": "missing-required-parameter",
"message": "Required path parameter userId is missing from Path Parameters section.",
"source": "openapi.yaml#/paths/~1v1~1users~1{userId}/get/parameters/0"
}
]
}
Important invariant:
Generated API reference is not publishable until verifier passes or human explicitly accepts exceptions.
26. OpenAPI Drift Detection
If OpenAPI changes, endpoint pages must be checked.
Drift examples:
- endpoint removed;
- path changed;
- request body field added;
- required field changed;
- response schema changed;
- auth changed;
- status code changed;
- operation deprecated;
- new endpoint added.
Drift artifact:
{
"schemaVersion": "api-drift.v1",
"oldContractHash": "sha256:old",
"newContractHash": "sha256:new",
"changes": [
{
"type": "response-schema-changed",
"endpointId": "http:get:/v1/users/{userId}",
"status": 200,
"severity": "high",
"affectedPages": [
"api/users/get-user.mdx"
]
}
]
}
CI behavior:
- non-breaking docs drift can warn;
- breaking API drift should fail unless docs updated;
- removed endpoint should require deprecation/removal docs.
27. Handling Multiple Specs
Monorepo may have:
services/
billing/openapi.yaml
identity/openapi.yaml
notification/openapi.yaml
Generator should support:
api:
specs:
- id: identity
title: Identity API
path: services/identity/openapi.yaml
output: api/identity
- id: billing
title: Billing API
path: services/billing/openapi.yaml
output: api/billing
Problems:
- duplicate schema names;
- duplicate operation IDs;
- inconsistent auth docs;
- shared components;
- cross-service examples;
- different API maturity.
Solution:
- namespace endpoint IDs by spec ID;
- namespace schema IDs by spec ID;
- allow shared auth docs;
- allow per-spec navigation group.
Endpoint ID:
http:identity:get:/v1/users/{userId}
http:billing:get:/v1/invoices/{invoiceId}
28. Integrating Human Notes
Generated endpoint pages need human-owned regions.
Example:
## Notes
{/* aidocs:manual:start */}
This endpoint is used by the onboarding flow and should not be called directly by internal batch jobs.
{/* aidocs:manual:end */}
When regenerating:
- preserve manual regions;
- move them if section still exists;
- warn if endpoint removed;
- never silently delete manual notes.
Human notes can also live in separate files:
docs/api/users/get-user.notes.md
or Logseq page:
pages/API - Get User.md
Part 037 will go deep into bidirectional sync.
For API reference generation, just define the merge boundary.
29. API Reference and Knowledge Graph
Every endpoint can become a knowledge node:
[[API Endpoint: GET /v1/users/{userId}]]
Properties:
type:: api-endpoint
method:: GET
path:: /v1/users/{userId}
operationId:: getUser
service:: identity
auth:: bearer
owner:: team-identity
docs:: [[Get User]]
source:: openapi.yaml
Relations:
GET /v1/users/{userId} -> returns -> User
GET /v1/users/{userId} -> implemented-by -> getUserHandler
GET /v1/users/{userId} -> tested-by -> get-user.test.ts
GET /v1/users/{userId} -> uses-auth -> BearerAuth
This is why API generation must produce structured artifact, not just MDX.
30. CLI Commands
Minimal commands:
aidocs api scan
aidocs api build
aidocs api verify
aidocs api diff
aidocs api nav
aidocs api preview
Examples:
aidocs api build --spec openapi.yaml --out docs/api
aidocs api diff --base main --head HEAD
aidocs api verify --strict
aidocs api build --spec services/*/openapi.yaml --mode explicit-mdx
Diagnostic output:
API Reference Build
Specs:
✓ openapi.yaml 3.1.0
Endpoints:
✓ 42 endpoints discovered
✓ 38 endpoints have examples
! 4 endpoints missing verified examples
Pages:
✓ 42 endpoint pages generated
✓ 6 concept pages generated
✓ docs.json navigation updated
Verification:
✓ method/path consistency
✓ required parameters
✓ response schemas
! 2 endpoints missing 401 docs
31. Testing Strategy
Test layers:
Unit tests
- OpenAPI parse;
$refresolution;- schema rendering;
- title generation;
- grouping;
- example selection;
- auth inheritance;
- response rendering.
Golden tests
Input:
fixtures/api/simple-openapi.yaml
Expected:
expected/docs/api/users/get-user.mdx
expected/api-reference.v1.json
expected/docs.json
Drift tests
- add endpoint;
- remove endpoint;
- add required field;
- change response type;
- mark endpoint deprecated.
Integration tests
- run
aidocs api build; - run verifier;
- render docs;
- validate links.
Mutation tests
- corrupt path param;
- remove required field;
- change status code;
- remove auth section;
- ensure verifier catches it.
32. Common Failure Modes
Failure Mode 1 — Schema dump disguised as docs
Symptom:
- page is technically complete but unusable.
Fix:
- separate overview, endpoint detail, schema reference;
- use field tables;
- include examples.
Failure Mode 2 — Invented examples
Symptom:
- response example has fields not in schema.
Fix:
- source examples from OpenAPI/tests/fixtures;
- label synthetic examples;
- verifier checks example schema compatibility.
Failure Mode 3 — Auth confusion
Symptom:
- endpoint docs say public, but middleware requires auth.
Fix:
- merge OpenAPI security with implementation middleware clues;
- produce warning on mismatch.
Failure Mode 4 — Navigation chaos
Symptom:
- 200 endpoint pages in one flat sidebar.
Fix:
- group by domain;
- create landing pages;
- hide low-value endpoints if configured;
- use navigation linting.
Failure Mode 5 — Stale reference
Symptom:
- OpenAPI changed, docs did not.
Fix:
- endpoint source hash;
- drift detection;
- CI gate.
33. Minimal Implementation Plan
Build in this order:
- parse OpenAPI YAML/JSON;
- normalize endpoints;
- resolve local
$ref; - render endpoint page skeleton;
- render params/request/responses;
- render examples;
- render auth;
- generate navigation;
- verify generated page;
- add drift detection;
- add human region preservation;
- add multi-spec support.
Do not start with LLM.
Most API reference generation can be deterministic.
LLM is useful for:
- summarizing endpoint purpose;
- writing friendly guide text;
- explaining complex schemas;
- generating group landing pages;
- rewriting raw descriptions;
- making troubleshooting notes.
But the core reference surface must come from contracts and source artifacts.
34. Exercise: Implement aidocs api build
Create a fixture:
fixtures/sample-api/openapi.yaml
With:
- 2 resources;
- 5 endpoints;
- bearer auth;
- one list endpoint with pagination;
- one create endpoint with request body;
- one deprecated endpoint;
- one reusable error schema.
Implement output:
docs/api/index.mdx
docs/api/authentication.mdx
docs/api/errors.mdx
docs/api/users/list-users.mdx
docs/api/users/create-user.mdx
docs/api/users/get-user.mdx
docs/api/orders/list-orders.mdx
docs/api/orders/get-order.mdx
docs.json
.aidocs/api-reference/api-reference.v1.json
.aidocs/reports/api-reference.verify.json
Acceptance criteria:
- every endpoint page has method/path;
- every required path parameter is documented;
- every request body field is rendered;
- every response status has a section;
- auth page exists;
- error page exists;
docs.jsonnavigation is stable;- verifier passes.
35. What You Should Understand Now
Setelah part ini, kamu harus memiliki mental model berikut:
- API reference bukan hasil LLM bebas.
- API reference adalah rendered view dari contract + source + example + review policy.
- OpenAPI adalah sumber utama, tetapi bukan satu-satunya sumber.
- Examples harus sourced, bukan dikarang.
- Navigation harus dirancang, bukan efek samping sorting path.
- Drift detection adalah bagian dari API docs, bukan fitur tambahan.
- MDX output harus punya provenance.
- Publisher adapter tidak boleh menghilangkan safety invariant.
Pada part berikutnya, kita naik dari endpoint-level docs ke architecture documentation generation: bagaimana sistem mengekstrak component view, runtime view, dependency view, sequence diagram, deployment view, dan dataflow view dari repo tanpa mengarang arsitektur palsu.
You just completed lesson 21 in build core. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.