Categorygithub.com/technicallyjosh/protoc-gen-openapi
modulepackage
1.12.0
Repository: https://github.com/technicallyjosh/protoc-gen-openapi.git
Documentation: pkg.go.dev

# README

protoc-gen-openapi

GitHub release (latest by date) lint status go report card

Yes, this is another protoc generator for OpenAPI. I created this for a couple reasons...

  • I wanted to learn protoc generation with a real-world problem.
  • The official google one sticks to gRPC and envoy standards. My team and I use Twirp and other REST frameworks. Sometimes you just want to define models and an API for docs.
  • Others try to do too much per the spec and fail to do the most common things well.

DISCLAIMER: This will be a limited subset of the OAPI3 specification. Not everything will make it in here. Why? Read the last bullet point above. :)

Some patterns were heavily inspired by gnostic.

Installation

go install github.com/technicallyjosh/protoc-gen-openapi@latest

Options

OptionDescriptionDefault
versionThe version of the API.0.0.1
titleThe title of the API.
descriptionA description of the API.
includeA list of proto package names to include only. ignore is ran after this
ignoreA list of proto package names to ignore delimited by pipes.
default_responseThe default response to be used.1
content_typeThe content type to be associated with all operations.1application/json
json_namesUse the JSON names that Protobuf provides. Otherwise, proto field names are used.false
json_outCreate a JSON file instead of the default YAML.false
hostThe host to be used for all operations.1
filenameSpecify the filename to output.openapi.yaml

1 Can be overridden on a file, service, or method.

Build Examples

Below are some basic examples on how to use this generator.

Protoc

protoc \
  --openapi_out=. \
  --openapi_opt=version=1.0.0 \
  --openapi_opt=title="My Awesome API" \
  api/some_service.proto

Buf

buf.yaml

# ... other things
deps:
  - buf.build/technicallyjosh/protoc-gen-openapi

buf.gen.yaml

plugins:
  - name: go
    out: api
    opt:
      - paths=source_relative
  - name: openapi
    strategy: all # important so all files are ran in the same generation.
    out: api
    opt:
      - title=My Awesome API
      - description=Look how awesome my API is!
      - ignore=module.v1|module.v2
      - default_response=SomeErrorObject

Basic Usage Example

syntax = "proto3";

import "oapi/v1/field.proto";
import "oapi/v1/file.proto";
import "oapi/v1/method.proto";
import "oapi/v1/service.proto";

option (oapi.v1.file) = {
  servers {
    url: "myawesomeapi.com"
  }

  security_schemes: {
    name: "bearer_auth"
    scheme: {
      type: "http"
      scheme: "bearer"
      bearer_format: "JWT"
    }
  }

  // Since this is at the file level, it's applied to all.
  security: {
    name: "bearer_auth"
    scopes: ["read:resource"]
  }
};

service MyService {
  option (oapi.v1.service) = {
    prefix: "/v1"
    x_display_name: "My Service"
  };

  rpc CreateSomething (CreateSomethingRequest) returns (CreateSomethingResponse) {
    option (oapi.v1.method) = {
      post: "create-something"
      summary: "Create Something"
      status: 201
    };
  }
}

message CreateSomethingRequest {
  // The name of something.
  // Example: something-awesome
  string name = 1 [(oapi.v1.required) = true];
}

message CreateSomethingResponse {
  // The ID of something.
  string id = 1;
  string name = 2;
}

Features

Note

Defining features is a work in progress. I aim to explain all that's possible the best I can.

Server definitions

You can define servers at the file, service, or method level. Each one overrides the previous. This allows for more advanced composition.

Example:

syntax = "proto3";

import "google/protobuf/empty.proto";
import "oapi/v1/file.proto";
import "oapi/v1/method.proto";
import "oapi/v1/service.proto";


option (oapi.v1.file) = {
  servers {
    url: "myawesomeapi.com" // file-defined for all services and methods
  }
};

service MyService {
  option (oapi.v1.service) = {
    servers {
      url: "myawesomeapi2.com" // overrides file-defined
    }
  };

  rpc CreateSomething (google.protobuf.Empty) returns (google.protobuf.Empty) {
    option (oapi.v1.method) = {
      servers {
        url: "myaweseomeapi3.com" // overrides service-defined
      }
    };
  }
}

Service Prefixes

Each service can have a path prefix set for all methods to inherit. This is useful when versioning your API or if you have a parameter that is defined for each method route.

You can override the entire path in the method by starting the path out with a /.

Example:

syntax = "proto3";

import "google/protobuf/empty.proto";
import "oapi/v1/file.proto";
import "oapi/v1/method.proto";
import "oapi/v1/service.proto";

option (oapi.v1.file) = {
  servers {
    url: "myawesomeapi.com"
  }
};

service MyService {
  option (oapi.v1.service) = {
    prefix: "/v1"
  };

  rpc CreateSomething (google.protobuf.Empty) returns (google.protobuf.Empty) {
    option (oapi.v1.method) = {
      post: "create" // becomes /v1/create
    };
  }

  rpc OverrideSomething (google.protobuf.Empty) returns (google.protobuf.Empty) {
    option (oapi.v1.method) = {
      get: "/create" // becomes /create
    };
  }
}

Features In Progress

  • Enum requirements on fields

Contributing

Coming... Right now I prefer that it's just me until I get a solid hold on generator patterns and the package is stable. I'm fully open to any suggestions though!

# Packages

No description provided by the author