atproto_openapi
Alpha, pre-release. Not yet published to Hex. Output shape and CLI flags are subject to change; expect breaking changes between 0.x releases.
A one-way emitter turning a set of atproto lexicons
into an OpenAPI 3.1.0 document,
built directly on atproto_lexicon's AST. Sibling of
atproto_sdl and atproto_mlf; a pure
transform plus a thin CLI.
Installation
gleam add atproto_openapi
Usage
import atproto_lexicon/decoding
import atproto_openapi
let assert Ok(doc) = decoding.decode_json(source)
let options = atproto_openapi.Options(title: "atproto", version: "0.0.0", servers: [])
let assert Ok(generated) = atproto_openapi.generate([doc], options)
generated.json // the OpenAPI document, as a JSON string
generated.operation_count // GET/POST paths emitted
generated.schema_count // components/schemas entries emitted
generated.subscriptions_skipped // subscription NSIDs excluded from paths
CLI, walking a directory of lexicon JSON and writing one OpenAPI document:
gleam run -m atproto_openapi -- <lexicons-dir> <out-file> [--title T] [--version V] [--server URL]
A lexicon file that fails to decode is reported and skipped rather than
aborting the run; --server may be repeated. The CLI prints a summary
(operation/schema counts) and names every subscription NSID it excluded
from paths, so the WSS gap is visible at the call site.
Modules
| Module | What it does |
|---|---|
atproto_openapi | Public API: generate turns lexicon docs into one OpenAPI 3.1.0 document; main runs the CLI. |
atproto_openapi/refs | Lexicon ref -> components/schemas key resolution; its RefError rides in the public Error. |
cli, emit, paths, and schema are internal.
What it does NOT do
- No
subscriptiondefs inpaths: WSS event streams aren't representable as OpenAPI operations. They're listed under thex-atproto-subscriptionsvendor extension instead. - No "try it out" server execution, no YAML output (JSON only for v1).
- This is a one-way transform: OpenAPI is never read back into a
LexiconDoc. - Output is compact JSON, not pretty-printed:
gleam_jsononly exposesjson.to_string, no pretty printer. Pipe through your formatter of choice (e.g.jq .) if you want it human-readable.