Skip to content

Endpoint catalog

An elaborate example that combines parsing, validation and traversal to emit a Markdown catalog of every endpoint in a spec — ready to paste into a README, wiki, or static site.

What it produces

For an OpenAPI file with 3 paths and 7 operations, the program prints:

markdown
# Orders API — Endpoint catalog

_Version 1.0.0 · generated by swaggercpp 0.2.1_

## `/orders`

### `GET /orders` — listOrders

Paginated list of orders.

**Parameters**

| Name   | In    | Required | Type    | Description       |
|--------|-------|----------|---------|-------------------|
| limit  | query | no       | integer | Page size (≤100). |
| cursor | query | no       | string  | Opaque cursor.    |

**Responses**

| Status | Description        |
|--------|--------------------|
| 200    | Page of orders.    |
| 400    | Invalid cursor.    |

### `POST /orders` — createOrder

Create a new order.

...

Code

cpp
#include <filesystem>
#include <iostream>
#include <sstream>
#include "swaggercpp/swaggercpp.hpp"

namespace sc = swaggercpp;

struct CatalogVisitor : sc::DocumentVisitor {
    std::ostringstream out;
    std::string        current_path;

    void enter_document(const sc::Document& doc) override {
        out << "# " << doc.info.title << " — Endpoint catalog\n\n"
            << "_Version " << doc.info.version
            << " · generated by swaggercpp_\n\n";
    }

    void enter_path_item(std::string_view path, const sc::PathItem&) override {
        current_path = path;
        out << "## `" << path << "`\n\n";
    }

    void enter_operation(std::string_view path,
                         sc::HttpMethod method,
                         const sc::Operation& op) override {
        const auto name = op.operation_id.value_or("(anonymous)");
        const auto upper = std::string(sc::to_string(method));
        std::string METHOD; METHOD.reserve(upper.size());
        for (char c : upper) METHOD.push_back(static_cast<char>(std::toupper(c)));

        out << "### `" << METHOD << ' ' << path << "` — " << name << "\n\n";
        if (op.summary)     out << op.summary.value()     << "\n\n";
        if (op.description) out << op.description.value() << "\n\n";

        if (!op.parameters.empty()) {
            out << "**Parameters**\n\n"
                << "| Name | In | Required | Type | Description |\n"
                << "|------|----|----------|------|-------------|\n";
            for (const auto& p : op.parameters) {
                out << "| `" << p.name << "` | " << sc::to_string(p.in)
                    << " | " << (p.required ? "yes" : "no")
                    << " | " << (p.schema && p.schema->type ? *p.schema->type : "-")
                    << " | " << p.description.value_or("")
                    << " |\n";
            }
            out << '\n';
        }

        if (!op.responses.empty()) {
            out << "**Responses**\n\n"
                << "| Status | Description |\n"
                << "|--------|-------------|\n";
            for (const auto& [code, resp] : op.responses) {
                out << "| " << code << " | " << resp.description << " |\n";
            }
            out << '\n';
        }
    }
};

int main(int argc, char** argv) {
    if (argc < 2) {
        std::cerr << "usage: catalog <openapi.yaml>\n";
        return 2;
    }

    auto r = sc::DocumentReader::read_file(argv[1]);
    if (!r) {
        for (const auto& i : r.issues())
            std::cerr << i.path << ": " << i.message << '\n';
        return 1;
    }

    sc::DocumentValidator validator;
    if (!validator.validate(r.value()).ok()) {
        std::cerr << "Spec has validation errors; refusing to emit catalog.\n";
        return 3;
    }

    CatalogVisitor v;
    sc::DocumentWalker::walk(r.value(), v);
    std::cout << v.out.str();
}

How it uses swaggercpp

  • DocumentReader::read_file — parse (YAML or JSON).
  • DocumentValidator::validate — refuse to emit a catalog for a broken spec.
  • DocumentVisitor + DocumentWalker::walk — traverse deterministically.
  • to_string(HttpMethod) / to_string(ParameterLocation) — stable, lowercase spec tokens.
  • std::optional / value_or — graceful defaults for missing summaries and descriptions.

Ideas for extensions

  • Emit HTML instead of Markdown.
  • Group by tag (Operation::tags) and sort within each group.
  • Inline component schemas when a parameter has $ref.
  • Persist to disk with DocumentWriter::write_file for a companion JSON export.

See also

Released under the MIT License.