Guide
Documenting an API
The .proto files are the one source of truth, and established tools turn them into the reference. peculiar-rpc adds one thing: the OpenAPI document without a running server.
Descriptor sets
Every step below reads a descriptor set, the compiled form of the .proto files with their comments kept:
protoc --include_imports --include_source_info --descriptor_set_out=api.binpb -I proto shop/v1/shop.proto
buf build --as-file-descriptor-set -o api.binpb does the same.
REST: OpenAPI
A server with Calls.openApi serves its document at /openapi.json. For a static site, peculiar-rpc-openapi prints the same document from a descriptor set:
nix run github:peculiar-systems/peculiar-rpc#peculiar-rpc-openapi -- \
--title Shop --version 1.1.0 --description "Things for sale." api.binpb > openapi.json
--service shop.v1.Shop limits it to the services named; by default it covers every service in the set. The document carries each binding's path, parameters and body, a schema for every message and enum, comments as descriptions, field_behavior as required, readOnly and writeOnly, and validation rules as schema constraints.
Any OpenAPI viewer renders it. With Scalar, one HTML file next to openapi.json is the whole reference:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Shop API</title>
</head>
<body>
<script
id="api-reference"
data-url="openapi.json"
data-configuration='{"showDeveloperTools":"never","agent":{"disabled":true},"mcp":{"disabled":true}}'
></script>
<script
src="https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.72.1/dist/browser/standalone.js"
integrity="sha384-U11tb2XnKvmwt8RlTvnwUnYgrN+ur4Xyh9htLhjajWNR/Oyl5AX5DEz00qRmlrmK"
crossorigin="anonymous"
></script>
</body>
</html>
The configuration turns off Scalar's AI agent and its MCP generation, which uploads the document to Scalar's service. Swagger UI and Redoc read the same file.
gRPC: protoc-gen-doc
protoc-gen-doc writes a reference of every service, method, message and enum, with their comments, as HTML or Markdown:
nix shell nixpkgs#protobuf nixpkgs#protoc-gen-doc -c \
protoc --doc_out=site --doc_opt=html,grpc.html -I proto shop/v1/shop.proto
It doesn't show buf.validate rules or google.api annotations. The OpenAPI document carries both.
Breaking changes: buf breaking
buf breaking compares a new descriptor set with the last released one. It fails on removed or renamed services, methods, fields and enum values, and on changed types:
buf breaking api.binpb --against released.binpb --exclude-imports
Run it in CI against the descriptor set of the last release. WIRE_JSON in buf.yaml's breaking.use checks only what breaks binary and JSON clients, not what breaks generated source.
It doesn't see three kinds of breaking change: a new required field, a stricter validation rule, and a changed HTTP binding. Diffing openapi.json between releases shows most of them: required properties, the rules that have a JSON Schema form, and the paths.