Documenting A GRPC API With OpenAPI
Documenting A GRPC API With OpenAPI
gRPC makes the specification and implementation of networked APIs a snap. But
what is the simplest way to document a gRPC API? There seem to be some hosted
providers by Google, e.g. SmartDocs, but I have yet to find a gRPC-specific tool. For
REST API frameworks, documentation is commonly generated along with live
examples using OpenAPI (formerly swagger). By using grpc-gateway it appears to
be pretty straight forward to generate a REST/gRPC API combo from protocol
buffers and then hook into the OpenAPI specification.
In this post, I’ll go through the creation of docs from gRPC protocol buffers. In a
following post, I’ll go through the creation of a live gRPC/REST service with Swagger
documentation.
We’ll create a simple “notes” service that has two endpoints: create a note and fetch
notes, optionally filtered.
syntax = "proto3";
package notes.v1;
service NoteService {
message Note {
uint64 id = 1;
string timestamp = 2;
string author = 3;
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 1/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
string text = 4;
bool private = 5;
message NoteFilter {
string before = 3;
string after = 4;
bool private = 5;
message Notebook {
Error error = 1;
message Error {
uint32 code = 1;
string message = 2;
So far, this is just a gRPC service definition. If we’re working in a Go project, we can
version our API and structure our project as follows:
Workspace/go/src/github.com/bbengfort/notes
└── cmd
| └── notes
| | └── main.go
├── go.mod
├── go.sum
└── proto
| └── notes
| | └── v1
| | | └── api.proto
Using this directory structure, generate the struct code and server and client
interfaces using protoc with go and grpc plugins:
$ protoc -I ./proto/ \
--go_out=. --go_opt=module=github.com/bbengfort/notes \
--go-grpc_out=. --go-grpc_opt=module=github.com/bbengfort/notes \
proto/notes/v1/*.proto
NOTE: You’ll have to install protoc (I did so with brew ) and the go and grpc
plugins (I used go get ). See the gRPC Go Quickstart for more information on
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 2/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
In this command the -I flag specifies where protoc can look for included
protocol buffer files (e.g. if they’re imported) - more on this later. The --go_out
and --go-grpc_out flags specify where to write the generated go code and the -
directory will be created with api.pb.go and api_grpc.pb.go inside of it. This is
because of the option go_package = "github.com/bbengfort/notes/v1"; directive
at the top of api.proto which resolves the output path based on all the module
directives.
In order to use the grpc-gateway and openapiv2 protocol buffer plugins, we’ll
have to modify our proto file with options that allow us to specify how the REST API
is defined and to supply information to the swagger.json generated OpenAPI v2
specification. Custom options are described in third party protocol buffer files that
must be included when we generate our protocol buffers using the -I flag.
NOTE: I believe there is a way to download and “install” third party libraries into
a global includes path, e.g. /usr/local/include/google/protobuf but I have to
investigate this further.
Workspace/go/src/github.com/bbengfort/notes
└── cmd
| └── notes
| | └── main.go
├── go.mod
├── go.sum
└── include
| └── googleapis
| | ├── LICENSE
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 3/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
| | | └── api
| | | | ├── annotations.proto
| | | | └── http.proto
| | | └── rpc
| | | | ├── code.proto
| | | | ├── error_details.proto
| | | | └── status.proto
| └── grpc-gateway
| | ├── LICENSE.txt
| | └── protoc-gen-openapiv2
| | | └── options
| | | | ├── annotations.proto
| | | | └── openapiv2.proto
└── proto
| └── notes
| | └── v1
| | | └── api.proto
└── v1
| ├── api.pb.go
| └── api_grpc.pb.go
Finally install the required grpc-gateway plugins so that you have protoc-gen-grpc-
If you’re using VSCode and the vscode-proto3 extension, then I like to add the
following directives to my workspace settings ( .vscode/settings.json ):
"protoc": {
"path": "/usr/local/bin/protoc",
"compile_on_save": false,
"options": [
"-I=${workspaceRoot}/proto",
"-I=${workspaceRoot}/includes/googleapis",
"-I=${workspaceRoot}/includes/grpc-gateway",
This prevents import error messages in your protocol buffers and enables
autocomplete.
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 4/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
Finally, we’re to the part we’ve been waiting for - annotating our proto file with the
REST and OpenAPI v2 options. At the beginning of api.proto add the following:
syntax = "proto3";
package notes.v1;
import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Notes";
version: "1.0";
contact: {
name: "bbengfort";
url: "https://github.com/bbengfort/notes";
email: "info@bengfort.com";
};
license: {
url: "https://github.com/bbengfort/notes/LICENSE";
};
};
schemes: HTTP;
schemes: HTTPS;
consumes: "application/json";
produces: "application/json";
};
Next we must update the service definition to map gRPC services to REST API calls:
service NoteService {
option (google.api.http) = {
get: "/api/v1/notes"
};
};
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 5/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
option (google.api.http) = {
post: "/api/v1/notes"
body: "*"
};
};
These options specify that the Fetch RPC can be accessed with a GET request to
/api/v1/notes and that the Create RPC uses POST to the same endpoint. Note
that the body: "*" flag ensures that the request body is included in endpoint.
In the final step of this post, we’ll use the openapiv2 plugin to generate the
swagger json specification and use the swagger-ui docker image to serve some
static documentation.
protoc -I ./proto/ \
-I include/googleapis -I include/grpc-gateway \
--go_out=. --go_opt=module=github.com/bbengfort/notes \
--go-grpc_out=. --go-grpc_opt=module=github.com/bbengfort/notes \
proto/notes/v1/*.proto
This protoc command has been updated to include the third party protocol buffer
files and also adds the openapiv2 plugin, writing a specification file at
openapiv2/notes/v1/api.swagger.json (note you may have to make the
openapiv2 directory before running this command).
-e SWAGGER_JSON=/openapiv2/notes/v1/api.swagger.json \
-v $PWD/openapiv2/:/openapiv2 \
swaggerapi/swagger-ui
This will pull the swaggerapi/swagger-ui image from DockerHub when you run it
for the first time. You can then view the docs at http://localhost/ :
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 6/7
10/14/22, 11:08 AM Documenting a gRPC API with OpenAPI | Libelli
The next steps are to use grpc-gateway to create a server that does both gRPC
hosting and a JSON REST API - complete with live Swagger documentation and
styling.
© 2021 Libelli
Powered by
Hugo &
PaperMod
https://bbengfort.github.io/2021/01/grpc-openapi-docs/ 7/7