Skip to content

Decouple OpenAPI spec generation from runtime #1616

@notrab

Description

@notrab

Problem

OpenAPI spec generation currently requires starting the ENSApi server. The generate_openapi_spec.sh script sets OPENAPI_GENERATE_MODE=true, boots the app with mock config, waits for readiness, then fetches /openapi.json over HTTP.

While functional, this adds unnecessary complexity:

  1. Requires a running server: starts ENSApi, polls for readiness, curls /openapi.json, then kills the process.
  2. Requires mock infrastructure: openapi-mock-config.ts exists only to fake DBs, RPCs, and indexers so the app can boot.
  3. Requires special middleware: openapi-generate-mode.middleware.ts blocks all routes except /openapi.json to avoid accidental use.
  4. Fragile in CI: polling, timeouts, and process cleanup add ~30s and introduce race conditions.

None of this should be necessary. hono-openapi exposes generateSpecs(app, options), which walks the registered Hono routes and collects describeRoute() metadata directly. No server, mocks, or HTTP required.

ENSApi

ensapi/src/index.ts does two things in one file:

  1. Constructs the app and registers middleware, mounts route handlers (which carry describeRoute metadata), configures OpenAPI documentation options.
  2. Starts the server and calls serve(), sets up graceful shutdown, warms caches.

Proposed Solution/Idea 1

Move all app construction out of src/index.ts into a new src/app.ts that exports the fully-configured Hono app and the OpenAPI documentation config.

We can then just pass the app/documentation to the server:

app.get("/openapi.json", openAPIRouteHandler(app, { documentation: openAPIDocumentation})

and for the script, we can use generateSpecs:

import {generateSpecs } from "hono-openapi";
import { app, openAPIDocumentation} from "./app";

const spec = await generateSpecs(app, {documentation: openAPIDocumentation });
process.stdout.write(JSON.stringify(spec, null, 2));

This should mean there is no mock server/config or polling needed, or processes to clean up.

Gotchas

We previously discovered an issue with the config import which calls buildConfigFromEnvironment(process.env and fetches the env vars/ensindexer config over HTTP.

To resolve this, we could move the route/middleware into functiojns that accept config as params rather than importing it at the module scope. That's likely a big chunk of work but much cleaner/pure.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    devopsDevOps relateddocsDocs related

    Type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions