Skip to content

Repository files navigation

protoc-gen-openapi

A protoc compiler plugin that converts gRPC service definitions (protobuf) into OpenAPI v3.1 specifications. Designed to pair with Envoy's GrpcJsonTranscoder: every compiler option that affects wire format has a corresponding Envoy filter option, so the generated OAS document describes what Envoy will actually emit. The plugin compiles to a native binary via GraalVM so it can be used directly in a protoc invocation without a JVM on PATH.

Subprojects

path description
root (src/) The plugin executable. Reads CodeGeneratorRequest from stdin and writes CodeGeneratorResponse to stdout, per the protoc plugin protocol.
model/ Protobuf definitions for the full OpenAPI v3.1 spec, plus the engine annotation extensions (annotations.proto, openapi.proto). These are the types the plugin reads from annotated .proto files.
examples/ Acceptance test suite. Each example is also an executable illustration of how to use a particular set of annotations or compiler options.

Usage

The plugin runs in two modes that can be mixed within a single proto file:

  • Convention-based — no engine annotations required. The plugin derives operations, path parameters, and request/response shapes from standard google.api.http bindings (or via autoMapping=true), and pulls info.title from the service name and info.version from the version option. See the conventions example for a complete working sample.
  • Annotation-based — import engine/protoc/openapi/annotations.proto and attach engine annotations to add or override anything the conventions can't express: explicit info, tags, security schemes, per-field schema constraints, etc. See the model README for the full annotation list and the examples for richer illustrations.

The plugin executable must be protoc-gen-openapi on PATH (or pointed at explicitly — see the Gradle examples below). Options are passed as --openapi_out=<comma-separated-options>:<outdir>.

Gradle

Configure the protobuf-gradle-plugin to invoke protoc-gen-openapi as a code-generation plugin.

plugins {
    id("com.google.protobuf") version "0.9.6"
}

// optional: include the annotations in your project
dependencies {
    protobuf("com.engine:protoc-gen-openapi-model:<version>")
}

protobuf {
    plugins {
        create("openapi") {
            // path to the native binary; or set `artifact = ...` once the published artifact is available
            artifact = "com.engine:protoc-gen-openapi:<version>"
        }
    }
    generateProtoTasks {
        all().all {
            plugins {
                create("openapi") {
                    option("version=1.0.0")
                    option("autoTagServices=true")
                    // other options as desired
                }
            }
        }
    }
}

Bash

# optional one-time: download and extract the model protos
curl -L -o protoc-gen-openapi-model-protos.zip \
  https://github.com/engine-public/protoc-gen-openapi/releases/download/<version>/protoc-gen-openapi-model-protos.zip
unzip -d build/proto/openapi-model protoc-gen-openapi-model-protos.zip

protoc \
  --proto_path=optional/path/to/extracted/model/protos \
  --proto_path=path/to/your/protos \
  --openapi_out=autoTagServices=true,otherOptionsAsDesired:./build/openapi \
  src/main/proto/example/v1/service.proto

Compiler Options

All options are defined on ProtocGenOpenAPI.Options. Click the option name to jump to its KDoc for full semantics, precedence rules, and Envoy interoperability notes.

name type default summary
alwaysPrintPrimitiveFields boolean false Add every non-repeated, non-message field to required. Pair with Envoy's always_print_primitive_fields.
autoMapping boolean false Auto-map gRPC methods without a google.api.http annotation to POST /<package>.<ServiceName>/<MethodName>. Mirrors Envoy's auto_mapping.
autoTagServices boolean false Tag every operation with its enclosing service name and emit a top-level tags entry per service using the service's proto comment as the description.
convertGrpcStatus boolean false Add a "default" response entry to every operation whose JSON body is an inline google.rpc.Status schema. Mirrors Envoy's convert_grpc_status.
enumValueFormat enum CANONICAL How enum values are written into OAS enum arrays: CANONICAL, NUMERIC_VALUE, or LOWER_CASE. Pair with Envoy's always_print_enums_as_ints / case_insensitive_enum_parsing.
inlineEnums boolean false Emit enum values inline at every reference instead of as a shared $ref in components/schemas.
inlineRequestSchemas boolean true Global default for inlining each RPC's request body schema at the use site. Per-method inline_request annotation overrides.
inlineResponseSchemas boolean true Global default for inlining each RPC's response body schema at the use site. Per-method inline_response annotation overrides.
logFile string File path the SLF4J binding writes records to. When unset, records go to standard error.
logLevel enum ERROR SLF4J threshold (TRACE, DEBUG, INFO, WARN, ERROR) applied to every logger the plugin and its dependencies create.
merge boolean false Combine every service across every target file into a single OpenAPI document instead of one document per service.
outputFormat enum JSON Serialization format of generated documents: JSON (default) or YAML.
preserveProtoFieldNames boolean false Use raw proto field names (e.g. my_field) as schema property keys instead of json_name or lowerCamelCase. Pair with Envoy's preserve_proto_field_names.
referenceLinkTarget enum NONE Renderer dialect that CommonMark reference links ([Type], [Service.Method]) in description fields resolve to: NONE (default — resolution off, brackets untouched), SWAGGER_UI (operations + tags), or REDOC (operations + tags + generated schema sections). When resolution is enabled, unresolved references are stripped of their brackets and rendered as an inline code span (`Property`), with a warning.
schemaNamespaceCasing enum NONE Case transformation applied to package segments of a namespaced schema key. NONE, CAPITALIZED, or UPPER_CASE.
schemaNamespaceSeparator enum NONE Separator placed between package segments of a namespaced schema key. NONE, UNDERSCORE, DASH, or DOT.
schemaNamespaceStrategy enum NONE Controls which package segments are prepended to schema keys in components/schemas. NONE, FULL_PACKAGE, or SIMPLIFIED_PACKAGE.
schemaNamespaceVersionExtraction boolean false Move package segments that look like proto API version identifiers (e.g. v1, v2beta1) to the end of the schema key.
serviceExclude regex Exclude services whose fully-qualified name contains a match, even if they also matched serviceInclude.
serviceInclude regex ^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*$ Only include services whose fully-qualified name contains a match. Schemas referenced only by excluded services are also omitted.
setSchemaTitleToProtoSimpleName boolean false Add a "title" field to each schema in components/schemas set to the unqualified proto type name.
streamNewlineDelimited boolean false Document server-streaming responses with content-type application/x-ndjson. Mirrors Envoy's stream_newline_delimited.
streamSseStyleDelimited boolean false Document server-streaming responses with content-type text/event-stream. Mirrors Envoy's stream_sse_style_delimited. Takes precedence over streamNewlineDelimited.
suppressDefaultEnumValues boolean false Omit enum values whose proto number is 0 (the proto3 default value convention) from all OAS enum value lists.
validateOutput boolean false Validate each generated document against the official OAS 3.1.1 schema. Issues are logged at WARN. See validationErrorsAreFatal to also fail the compile.
validationErrorsAreFatal boolean false When true, validation issues are also written to the protoc error and fail the compile. No effect unless validateOutput is also true.
version string Fallback info.version for documents whose annotations do not specify one.

Enum-valued options accept their values case-insensitively.

google.api.http body modes

The plugin reads the body field of every google.api.http annotation and maps it to OpenAPI as specified in google/api/http.proto — the same semantics enforced at runtime by Envoy's gRPC-JSON transcoder. The three modes are identical across all five verbs (GET, POST, PUT, PATCH, DELETE):

body value HTTP request body URL query parameters
unset / "" none every request field not bound to a {var} in the URL template
"*" the whole request message none
"<field_name>" the value of that single top-level field every other request field not bound to the URL template

Auto-derived query parameters use the field's JSON name by default (or the snake_case proto name when preserveProtoFieldNames is set), recurse into nested messages with dotted names (?address.city=…), and emit style=form, explode=true for repeated scalars. Repeated message and map fields are skipped with a WARN log since OpenAPI has no faithful query-string representation for them. Each parameter's schema carries the field's own (engine.protoc.openapi.field) annotation (constraints, format, enum, …) merged on top of its proto-derived type, so a query parameter advertises the same constraints the field declares everywhere else.

When a method also declares (engine.protoc.openapi.parameters) entries, those manual declarations win for any field they cover and a WARN log records the overlap. A field is treated as covered when a manual parameter's name matches its proto name, its JSON name, or — for nested fields — its dotted name (address.city); manual entries that reference a reusable component via $ref are matched by the referenced component's declared name. Two coverage cases cannot be detected and may surface as duplicate parameters: a $ref the plugin cannot resolve locally (a remote/relative $ref, or one whose component is itself a $ref), and a nested field bound to the URL with a dotted placeholder such as {address.city} (the path-template parser only recognises single-segment {var} placeholders). Declare the overlapping field with an inline parameter, or move it into the request body, to avoid the duplicate. Worked examples live in examples/src/envoy/ (live Envoy round-trip tests) and examples/src/complete/ (snapshot coverage of every verb/body combination, plus manual-override precedence).

Related Projects

  • engine-public/protoc-utils — shared protoc plugin utilities (descriptor wrappers, comment parsing, parameter handling) and the recorder plugin used by this project's example suite.

Contributing

See CONTRIBUTING.md for build commands, the native-image / reflection metadata workflow, and the PR process.

About

protoc compiler to turn gRPC services into openapi v3.1 specs

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages