Skip to content

[docs] Generate OpenAPI spec from inline route annotations #6377

@orangesurf

Description

@orangesurf

Background

The API docs at mempool.space/docs are currently driven by frontend/src/app/docs/api-docs/api-docs-data.ts.

This approach has served us well to date but it has some real costs:

  • Drift: When a dev adds or changes a route, they have to separately update the api-docs.
  • Duplication: Example responses are copy-pasted inline, nothing is generated or validated.
  • No machine-readable spec: There is no OpenAPI file that tooling can consume.
  • Limited Customisation: It's hard to go beyond the current list layout for API docs without lots of wrangling.

What this new approach unlocks

Moving to inline @openapi annotations on route handlers and generating a standard OpenAPI spec enables:

  • Single source of truth: The docs live next to the code making it easy to update the annotation in the same PR as any change to the route.
  • Machine-readable spec: We will have an openapi.yml that works with many tools out of the box.
  • CI enforcement: A build step regenerates the spec and diffs against what's committed meaning undocumented route changes fail the build.
  • Auto-generated docs page: Ultimately, the existing frontend docs page can be replaced with one driven from the generated spec instead of the hand-maintained TS file.
  • Auto-generated client libraries: A valid OpenAPI spec can be used to auto-generate the mempoolJS client library instead of hand-maintaining it.
  • MCP server: A spec-driven MCP server can expose the full mempool API to AI tooling, agents, and integrations.

Proposal

Put the OpenAPI definitions directly in the mempool source, as @openapi YAML blocks inside JSDoc comments above each route handler. A small collector tool (runs as a build step) scans the source and assembles everything into a single openapi.yml.

What this looks like in practice

A mempool REST API endpoint:

/**
 * @openapi
 * /v1/mining/pool/{slug}:
 *   get:
 *     summary: Get mining pool info
 *     description: Returns details about the mining pool specified by `slug`.
 *     tags:
 *       - Mining
 *     parameters:
 *       - name: slug
 *         in: path
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       '200':
 *         description: Mining pool details
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/MiningPool'
 */
app.get(config.MEMPOOL.API_URL_PREFIX + 'mining/pool/:slug', async (req, res) => {
  // ...
});

An Esplora API endpoint:

/**
 * @openapi
 * /address/{address}:
 *   get:
 *     summary: Get address info
 *     description: Returns details about an address, including confirmed and unconfirmed balance and transaction statistics.
 *     tags:
 *       - Addresses
 *     parameters:
 *       - name: address
 *         in: path
 *         required: true
 *         schema:
 *           type: string
 *     responses:
 *       '200':
 *         description: Address details
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/Address'
 */
app.get(config.MEMPOOL.API_URL_PREFIX + 'address/:address', async (req, res) => {
  // ...
});

Schemas

Schemas (e.g. Address, Transaction, Block, MiningPool) are defined once in a central file using the same @openapi annotation pattern. Route annotations reference them via $ref, for example $ref: '#/components/schemas/Address'. The collector tool merges them into the spec's components.schemas section. This eliminates the response duplication.

Handling the /api/ vs /api/v1/ prefix

The mempool.space API is two separate APIs behind one domain:

Layer Prefix Served by Endpoints
Esplora API /api/ electrs (Rust) Addresses, transactions, blocks, mempool basics
Mempool REST API /api/v1/ Mempool backend (Node.js, port 8999) Fees, mining, lightning, accelerator, services

In production, nginx routes /api/ to electrs and /api/v1/ to the mempool backend. In simple self-hosted setups without electrs, the mempool backend reimplements the Esplora endpoints in bitcoin.routes.ts and nginx rewrites /api/ to /api/v1/ so both prefixes hit the same process.

The existing docs page already handles this simply: the template always prepends /api to the urlString, and the urlString either includes /v1/ or doesn't. The OpenAPI spec should follow the same convention with a single server (https://mempool.space/api) and the /v1/ included in the path string where it belongs:

  • /v1/fees/recommended (mempool REST API)
  • /address/{address} (Esplora API)

Alternately there is the option to use OpenAPI server-override if we feel that is superior.

Collector tool

A standalone script (no runtime dependency) that:

  1. Recursively finds all .ts files under backend/src/
  2. Extracts YAML from @openapi JSDoc blocks
  3. Merges everything onto a base template (info, servers, tags)
  4. Writes openapi.yml
  5. Optionally validates that all $ref pointers resolve

Run as npm run docs or in CI to catch undocumented changes.

WebSocket events

The mempool API also has WebSocket channels (live blocks, mempool stats, address tracking, transaction tracking). TBC: these may be documentable using @asyncapi annotations on the WebSocket handlers, with the collector producing a separate asyncapi.yml.

Implementation path

Phase 1: OpenAPI Spec Generation

  1. Add the collector tool to the mempool repo
  2. Define shared schemas (Address, Transaction, Block, MiningPool, etc.) in a central file
  3. Add @openapi annotations with descriptions and schema refs to each route handler
  4. Add a CI check that regenerates the spec and diffs against what's committed

Phase 2: Use the spec

  1. Replace the frontend docs page with one driven from the generated spec instead of api-docs-data.ts
  2. Add x-code-samples for mempoolJS SDK examples and curl snippets to reach feature parity with the current docs if we feel these are needed.
  3. Auto-generate the mempoolJS client library from the spec
  4. Build an MCP server from the spec for AI tooling and agent integrations
  5. Retire the hand-maintained api-docs-data.ts

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions