Technical Documentation: grpc-ecosystem/grpc-gateway
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
About
The gRPC-Gateway is a plugin of the Google protocol buffers compiler
protoc.
It reads protobuf service definitions and generates a reverse-proxy server which
translates a RESTful HTTP API into gRPC. This server is generated according to thegoogle.api.http
annotations in your service definitions.
This helps you provide your APIs in both gRPC and RESTful style at the same time.
Docs
You can read our docs at:
Testimonials
We use the gRPC-Gateway to serve millions of API requests per day,
and have been since 2018 and through all of that,
we have never had any issues with it.- William Mill, Ad Hoc
Background
gRPC is great -- it generates API clients and server stubs in many programming
languages, it is fast, easy-to-use, bandwidth-efficient and its design is
combat-proven by Google. However, you might still want to provide a traditional
RESTful JSON API as well. Reasons can range from maintaining
backward-compatibility, supporting languages or clients that are not well supported by
gRPC, to simply maintaining the aesthetics and tooling involved with a RESTful
JSON architecture.
This project aims to provide that HTTP+JSON interface to your gRPC service.
A small amount of configuration in your service to attach HTTP semantics is all
that's needed to generate a reverse-proxy with this library.
Installation
Compile from source
The following instructions assume you are using
Go Modules for dependency
management. Use a
tool dependency
to track the versions of the following executable packages:
For Go 1.24 and later, prefer the tool directive in go.mod described in the "Tracking Tools in go.mod" section. The following blank-import pattern is mainly useful for projects that are not using Go 1.24 yet.
// +build tools
package tools
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)Run go mod tidy to resolve the versions. Install by running
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpcThis will place four binaries in your $GOBIN;
protoc-gen-grpc-gatewayprotoc-gen-openapiv2protoc-gen-goprotoc-gen-go-grpc
Make sure that your $GOBIN is in your $PATH.
If you want to emit OpenAPI 3.1 instead of (or in addition to) Swagger 2.0,
install protoc-gen-openapiv3 as well:
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3⚠️ Alpha.
protoc-gen-openapiv3is new and its output is not yet
stable — the emitted JSON shape for oneofs, wrappers, enums, and similar
constructs may change in response to real-world feedback before the
plugin graduates. Don't rely on the exact spec bytes being stable across
minor releases; the proto-to-OpenAPI mapping rules are likely to
tighten, and tooling-compatibility compromises (documented in the
generator source) may be revisited as consumer OpenAPI 3.1 support
matures.protoc-gen-openapiv2is the production-stable option.
See OpenAPI 3.1 Output
for what it supports and how it differs from protoc-gen-openapiv2.
**Using the `tool` Directive in Go 1.24**
Starting from Go 1.24, the tool directive in go.mod provides a structured way to track and manage executable dependencies. This replaces the previous workaround of using a separate tools.go file with blank imports.
**Tracking Tools in `go.mod`**
Instead of manually importing tool dependencies in a Go source file, you can now use the tool directive in go.mod to declare the tools your project depends on. For example:
module tools
go 1.24
tool (
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
google.golang.org/grpc/cmd/protoc-gen-go-grpc
google.golang.org/protobuf/cmd/protoc-gen-go
)**Managing Tool Dependencies**
To add tools to your module, use the -tool flag with go get:
go get -tool github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go get -tool github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
go get -tool google.golang.org/protobuf/cmd/protoc-gen-go
go get -tool google.golang.org/grpc/cmd/protoc-gen-go-grpcThis automatically updates go.mod, adding the tools under the tool directive along with require statements to ensure version tracking.
Install Tools
Once the tool dependencies are properly recorded in the go.mod file, simply execute the following command in the root directory of your project:
go install toolThis will place four binaries in your $GOBIN;
protoc-gen-grpc-gatewayprotoc-gen-openapiv2protoc-gen-goprotoc-gen-go-grpc
Make sure that your $GOBIN is in your $PATH.
Download the binaries
You may alternatively download the binaries from the GitHub releases page.
We generate SLSA3 signatures using the OpenSSF's slsa-framework/slsa-github-generator during the release process. To verify a release binary:
- Install the verification tool from slsa-framework/slsa-verifier#installation.
- Download the provenance file
attestation.intoto.jsonlfrom the GitHub releases page. - Run the verifier:
slsa-verifier -artifact-path <the-binary> -provenance attestation.intoto.jsonl -source github.com/grpc-ecosystem/grpc-gateway -tag <the-tag>Alternatively, see the section on remotely managed plugin versions below.
Usage
1.Define your [gRPC](https://grpc.io/docs/) service using protocol buffers
your_service.proto:
syntax = "proto3";
package your.service.v1;
option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1";
message StringMessage {
string value = 1;
}
service YourService {
rpc Echo(StringMessage) returns (StringMessage) {}
}2. Generate gRPC stubs
This step generates the gRPC stubs that you can use to implement the service and consume from clients:
Here's an example buf.gen.yaml you can use to generate the stubs with buf:
version: v2
plugins:
- local: protoc-gen-go
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-go-grpc
out: gen/go
opt:
- paths=source_relativeWith this file in place, you can generate your files using buf generate.
For a complete example of using
buf generateto generate protobuf stubs, see
the boilerplate repo.
For more information on generating the stubs with buf, see
the official documentation.
If you are using protoc to generate stubs, here's an example of what a command
might look like:
protoc -I . \
--go_out ./gen/go/ --go_opt paths=source_relative \
--go-grpc_out ./gen/go/ --go-grpc_opt paths=source_relative \
your/service/v1/your_service.proto3. Implement your service in gRPC as usual.
4. Generate reverse-proxy using `protoc-gen-grpc-gateway`
At this point, you have 3 options:
- no further modifications, use the default mapping to HTTP semantics (method, path, etc.)
- this will work on any
.protofile, but will not allow setting HTTP paths, request parameters or similar
- this will work on any
- additional
.protomodifications to use a custom mapping- relies on parameters in the
.protofile to set custom HTTP mappings
- relies on parameters in the
- no
.protomodifications, but use an external configuration file- relies on an external configuration file to set custom HTTP mappings
- mostly useful when the source proto file isn't under your control
1. Using the default mapping
This requires no additional modification to the .proto file but does require enabling a specific option when executing the plugin.
The generate_unbound_methods should be enabled.
Here's what a buf.gen.yaml file might look like with this option enabled:
version: v2
plugins:
- local: protoc-gen-go
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-go-grpc
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-grpc-gateway
out: gen/go
opt:
- paths=source_relative
- generate_unbound_methods=trueWith protoc (just the grpc-gateway stubs):
protoc -I . --grpc-gateway_out ./gen/go \
--grpc-gateway_opt paths=source_relative \
--grpc-gateway_opt generate_unbound_methods=true \
your/service/v1/your_service.proto2. With custom annotations
Add a google.api.http
annotation to your .proto file
your_service.proto:
syntax = "proto3";
package your.service.v1;
option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1";
+
+import "google/api/annotations.proto";
+
message StringMessage {
string value = 1;
}
service YourService {
- rpc Echo(StringMessage) returns (StringMessage) {}
+ rpc Echo(StringMessage) returns (StringMessage) {
+ option (google.api.http) = {
+ post: "/v1/example/echo"
+ body: "*"
+ };
+ }
}You will need to provide the required third party protobuf files to the protobuf compiler.
If you are using buf, this dependency can
be added to thedepsarray in yourbuf.yamlunder the namebuf.build/googleapis/googleapis:yamlversion: v2 name: buf.build/yourorg/myprotos deps: - buf.build/googleapis/googleapisAlways run
buf dep updateafter adding a dependency to yourbuf.yaml.
See a_bit_of_everything.proto
for examples of more annotations you can add to customize gateway behavior
and generated OpenAPI output.
Here's what a buf.gen.yaml file might look like:
version: v2
plugins:
- local: protoc-gen-go
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-go-grpc
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-grpc-gateway
out: gen/go
opt:
- paths=source_relativeIf you are using protoc to generate stubs, you need to ensure the required
dependencies are available to the compiler at compile time. These can be found
by manually cloning and copying the relevant files from the
googleapis repository, and providing
them to protoc when running. The files you will need are:
google/api/annotations.proto
google/api/field_behavior.proto
google/api/http.proto
google/api/httpbody.protoHere's what a protoc execution might look like:
protoc -I . --grpc-gateway_out ./gen/go \
--grpc-gateway_opt paths=source_relative \
your/service/v1/your_service.proto3. External configuration
If you do not want to (or cannot) modify the proto file for use with gRPC-Gateway you can
alternatively use an external
gRPC Service Configuration file.
Check our documentation
for more information. This is best combined with the standalone=true option
to generate a file that can live in its own package, separate from the files
generated by the source protobuf file.
Here's what a buf.gen.yaml file might look like with this option enabled:
version: v2
plugins:
- local: protoc-gen-go
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-go-grpc
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-grpc-gateway
out: gen/go
opt:
- paths=source_relative
- grpc_api_configuration=path/to/config.yaml
- standalone=trueWith protoc (just the grpc-gateway stubs):
protoc -I . --grpc-gateway_out ./gen/go \
--grpc-gateway_opt paths=source_relative \
--grpc-gateway_opt grpc_api_configuration=path/to/config.yaml \
--grpc-gateway_opt standalone=true \
your/service/v1/your_service.proto5. Write an entrypoint for the HTTP reverse-proxy server
package main
import (
"context"
"flag"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/grpclog"
gw "github.com/yourorg/yourrepo/proto/gen/go/your/service/v1/your_service" // Update
)
var (
// command-line options:
// gRPC server endpoint
grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:9090", "gRPC server endpoint")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// Register gRPC server endpoint
// Note: Make sure the gRPC server is running properly and accessible
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
return err
}
// Start HTTP server (and proxy calls to gRPC server endpoint)
return http.ListenAndServe(":8081", mux)
}
func main() {
flag.Parse()
if err := run(); err != nil {
grpclog.Fatal(err)
}
}6. (Optional) Generate OpenAPI definitions using `protoc-gen-openapiv2`
Here's what a buf.gen.yaml file might look like:
version: v2
plugins:
- local: protoc-gen-go
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-go-grpc
out: gen/go
opt:
- paths=source_relative
- local: protoc-gen-grpc-gateway
out: gen/go
opt:
- paths=source_relative
- generate_unbound_methods=true
- local: protoc-gen-openapiv2
out: gen/goTo use the custom protobuf annotations supported by protoc-gen-openapiv2, we need
another dependency added to our protobuf generation step. If you are usingbuf, you can add the buf.build/grpc-ecosystem/grpc-gateway dependency
to your deps array:
version: v2
name: buf.build/yourorg/myprotos
deps:
- buf.build/googleapis/googleapis
- buf.build/grpc-ecosystem/grpc-gatewayWith protoc (just the swagger file):
protoc -I . --openapiv2_out ./gen/openapiv2 \
your/service/v1/your_service.protoIf you are using protoc to generate stubs, you will need to copy the protobuf
files from the protoc-gen-openapiv2/options directory of this repository,
and providing them to protoc when running.
Note that this plugin also supports generating OpenAPI definitions for unannotated methods;
use the generate_unbound_methods option to enable this.
It is possible with the HTTP mapping for a gRPC service method to create duplicate mappings
with the only difference being constraints on the path parameter.
/v1/{name=projects/*} and /v1/{name=organizations/*} both become /v1/{name}. When
this occurs the plugin will rename the path parameter with a "_1" (or "_2" etc) suffix
to differentiate the different operations. So in the above example, the 2nd path would become/v1/{name_1=organizations/*}. This can also cause OpenAPI clients to URL encode the "/" that is
part of the path parameter as that is what OpenAPI defines in the specification. To allow gRPC gateway to
accept the URL encoded slash and still route the request, use the UnescapingModeAllCharacters or
UnescapingModeLegacy (which is the default currently though may change in future versions). See
Customizing Your Gateway
for more information.
Alternative: OpenAPI 3.1 with `protoc-gen-openapiv3`
⚠️ Alpha.
protoc-gen-openapiv3is new and its output is not yet
stable. Expect breaking changes to the emitted JSON shape (oneof
encodings, wrapper types, enum form, path-template expansion, etc.)
between minor releases while the mapping rules settle in response to
real-world feedback. Use it if you want to follow that evolution;
otherwiseprotoc-gen-openapiv2remains the stable choice.
protoc-gen-openapiv3 is a separate generator that emits OpenAPI
3.1.0 JSON directly from google.api.http annotations. It's a good
choice when your downstream tooling requires 3.1, when you want the richer
JSON Schema 2020-12 constructs (proper oneOf encoding for proto oneofs,
spec-accurate literal expansion of constrained path templates like{name=shelves/*/books/*}, etc.).
Add it to your buf.gen.yaml alongside (or instead of) protoc-gen-openapiv2:
version: v2
plugins:
- local: protoc-gen-openapiv3
out: gen/openapiv3Or invoke it directly with protoc:
protoc -I. --openapiv3_out=./gen/openapiv3 your/service/v1/your_service.protoA .openapi.json file appears next to each proto file that declares HTTP
bindings; files with no HTTP-bound services produce no output.
See OpenAPI 3.1 Output for
more information about the capabilities of protoc-gen-openapiv3.
Usage with remote plugins
As an alternative to all of the above, you can use buf with
remote plugins
to manage plugin versions and generation. An example buf.gen.yaml using remote
plugin generation looks like this:
version: v2
plugins:
- remote: buf.build/protocolbuffers/go:v1.31.0
out: gen/go
opt:
- paths=source_relative
- remote: buf.build/grpc/go:v1.3.0
out: gen/go
opt:
- paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway:v2.16.2
out: gen/go
opt:
- paths=source_relative
- remote: buf.build/grpc-ecosystem/openapiv2:v2.16.2
out: gen/openapiv2This requires no local installation of any plugins. Be careful to use the same
version of the generator as the runtime library, i.e. if using v2.16.2, run
$ go get github.com/grpc-ecosystem/grpc-gateway/[email protected]To get the same version of the runtime in your go.mod.
Note that usage of remote plugins is incompatible with usage of external configuration files like grpc_api_configuration.
Video intro
This GopherCon UK 2019 presentation from our maintainer @JohanBrandhorst provides a good intro to using the gRPC-Gateway. It uses the following boilerplate repo as a base: https://github.com/johanbrandhorst/grpc-gateway-boilerplate.
Parameters and flags
When using buf to generate stubs, flags and parameters are passed through
the opt field in your buf.gen.yaml file, for example:
version: v2
plugins:
- local: protoc-gen-grpc-gateway
out: gen/go
opt:
- paths=source_relative
- grpc_api_configuration=path/to/config.yaml
- standalone=trueDuring code generation with protoc, flags to gRPC-Gateway tools must be passed
through protoc using one of 2 patterns:
- as part of the
--<tool_suffix>_outprotocparameter:--<tool_suffix>_out=<flags>:
--grpc-gateway_out=repeated_path_param_separator=ssv:.
--openapiv2_out=repeated_path_param_separator=ssv:.- using additional
--<tool_suffix>_optparameters:--<tool_suffix>_opt=<flag>[,<flag>]*
--grpc-gateway_opt repeated_path_param_separator=ssv
--openapiv2_opt repeated_path_param_separator=ssvMore examples
More examples are available under the examples/internal directory.
proto/examplepb/echo_service.proto,proto/examplepb/a_bit_of_everything.proto,proto/examplepb/unannotated_echo_service.proto: service definitionproto/examplepb/echo_service.pb.go,proto/examplepb/a_bit_of_everything.pb.go,proto/examplepb/unannotated_echo_service.pb.go: [generated] stub of the serviceproto/examplepb/echo_service.pb.gw.go,proto/examplepb/a_bit_of_everything.pb.gw.go,proto/examplepb/unannotated_echo_service.pb.gw.go: [generated] reverse proxy for the serviceproto/examplepb/unannotated_echo_service.yaml: gRPC API Configuration forunannotated_echo_service.proto
server/main.go: service implementationcmd/example-gateway-server/: entrypoint of the generated reverse proxy
To use the same port for custom HTTP handlers (e.g. serving swagger.json),
gRPC-Gateway, and a gRPC server, see
this example by CoreOS
(and its accompanying blog post).
This example by neiro.ai (and its accompanying blog post) shows how mediafiles using multipart/form-data can be integrated into rpc messages using a middleware.
See also these community example repositories:
- grpc-gateway-boilerplate: a starter/boilerplate for new gRPC-Gateway projects.
- grpc-template: an example using Chi as the HTTP router, a custom base path, and embedded Swagger UI.
Features
Supported
- Generating JSON API handlers.
- Method parameters in the request body.
- Method parameters in the request path.
- Method parameters in the query string.
- Enum fields in the path parameter (including repeated enum fields).
- Mapping streaming APIs to newline-delimited JSON streams.
- Mapping HTTP headers with
Grpc-Metadata-prefix to gRPC metadata (prefixed withgrpcgateway-) - Optionally emitting API definitions for
OpenAPI (Swagger) v2. - Setting gRPC timeouts
through inbound HTTPGrpc-Timeoutheader. - Partial support for gRPC API Configuration
files as an alternative to annotation. - Automatically translating PATCH requests into Field Mask gRPC requests. See
the docs
for more information. - Protobuf Editions support (edition 2023).
- Go Opaque API support.
No plan to support
But patches are welcome.
- Method parameters in HTTP headers.
- Handling trailer metadata.
- Encoding request/response body in XML.
- True bi-directional streaming.
Mapping gRPC to HTTP
- How gRPC error codes map to HTTP status codes in the response.
- HTTP request source IP is added as
X-Forwarded-ForgRPC request header. - HTTP request host is added as
X-Forwarded-HostgRPC request header. - HTTP
Authorizationheader is added asauthorizationgRPC request header. - Remaining Permanent HTTP header keys (as specified by the IANA
here)
are prefixed withgrpcgateway-and added with their values to gRPC request
header. - HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata
(prefixed withgrpcgateway-). - While configurable, the default {un,}marshaling uses
protojson. - The path template used to map gRPC service methods to HTTP endpoints supports the google.api.http
path template syntax. For example,/api/v1/{name=projects/*/topics/*}or/prefix/{path=organizations/**}.
Contribution
See CONTRIBUTING.md.
License
gRPC-Gateway is licensed under the BSD 3-Clause License.
See LICENSE for more details.
Overview (`openapiv3-merge/README.md`)
openapiv3-merge
openapiv3-merge combines multiple OpenAPI 3.1 JSON documents into a single
document. It is the companion tool for protoc-gen-openapiv3,
which emits one document per proto file. Pipe its output throughopenapiv3-merge to produce a single combined spec.
Why a separate binary
protoc-gen-openapiv3 follows the protobuf convention of one output file per
input file. That keeps the plugin well-behaved under all protoc/buf
invocations and avoids forcing buf users onto strategy: all. The trade-off
is that consumers who want one document have to combine the per-file outputs
themselves. openapiv3-merge is that step.
Install
go install github.com/grpc-ecosystem/grpc-gateway/v2/openapiv3-merge@latestUsage
openapiv3-merge FILE [FILE ...] > merged.openapi.jsonThe merged document is written to stdout. Errors go to stderr and the
process exits with a non-zero status. Input order matters: the first
input's info, servers, and externalDocs are kept in the merged
document, so put the file with the broadest scope first.
With buf
#!/usr/bin/env bash
set -euo pipefail
buf generate
# Put the file with the shared openapiv3_document annotation first so
# its info/servers/externalDocs are kept; sort the rest for determinism.
root=gen/api/v1/api.openapi.json
mapfile -t rest < <(find gen -name '*.openapi.json' ! -path "$root" | sort)
openapiv3-merge "$root" "${rest[@]}" > gen/api.openapi.jsonWith protoc
#!/usr/bin/env bash
set -euo pipefail
protoc -I. \
--openapiv3_out=./gen \
$(find . -name '*.proto')
root=gen/api/v1/api.openapi.json
mapfile -t rest < <(find gen -name '*.openapi.json' ! -path "$root" | sort)
openapiv3-merge "$root" "${rest[@]}" > gen/api.openapi.jsonOne-shot
For trivial trees where any input may go first:
openapiv3-merge $(find . -name '*.openapi.json' | sort) > api.openapi.jsonMerge rules
The merger is strict: anything that could be a silent overwrite is rejected.
| Field | Rule |
|---|---|
openapi |
Must match across all inputs. |
info, servers, externalDocs |
Taken from the first input. Later inputs' values are dropped. |
paths, webhooks |
Unioned in input order. Same path with non-identical content → error. |
components/* |
Unioned with sorted keys. Same name with non-identical content → error. |
tags |
Deduplicated by name. Same name with non-identical metadata → error. |
security |
First input to declare a non-empty array wins. Later inputs that declare a different non-empty array → error. |
Unknown top-level keys (extensions, x-*) |
Taken from the first input. |
"Non-identical content" is compared canonically: two values that differ only
in key order are equal. This means component schemas with the same fully
qualified name (which is what protoc-gen-openapiv3 emits — e.g.example.v1.User) merge cleanly across packages.
Output
- Top-level fields appear in OpenAPI 3.1.0 declaration order.
pathsandwebhooksare emitted in input order.components/*sub-maps are sorted by key, matchingprotoc-gen-openapiv3.- Tags appear in first-occurrence order.
Overview (`examples/internal/README.md`)
One way to run the example
# Handle dependencies (this repository uses Go modules)
$ go mod downloadFollow the guides from this README.md to run the server and gateway.
# Make sure you are in the correct directory:
# $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal
$ cd examples/internal/browser
$ pwd
# Install gulp
$ npm install gulp-cli
$ npm install
$ gulp
# Run
$ gulp bower
$ gulp backendsThen you can use curl or a browser to test:
# List all apis
$ curl http://localhost:8080/openapiv2/echo_service.swagger.json
# Visit the apis
$ curl -XPOST http://localhost:8080/v1/example/echo/foo
{"id":"foo"}
$ curl http://localhost:8080/v1/example/echo/foo/123
{"id":"foo","num":"123"}So you have visited the apis by HTTP successfully. You can also try other apis.
Overview (`internal/casing/README.md`)
Case conversion
This package contains two functions:
Camelcopied from thegithub.com/golang/protobuf/protoc-gen-go/generatorpackage.JSONCamelCasecopied from thegithub.com/protocolbuffers/protobuf-go/internal/strspackage.
Both these modules are licensed by The Go Authors, as reflected in this package's [LICENSE.md].
Overview (`examples/internal/browser/README.md`)
Browser example
This directory contains an example use of gRPC-Gateway with web browsers.
The following commands automatically runs integration tests with phantomjs.
$ npm install gulp-cli
$ npm install
$ gulpOther examples
Very simple example
Run
$ gulp bower
$ gulp backendsthen, open index.html.
Integration test with your browser
Run
$ gulp servethen, open http://localhost:8000 with your browser.
Index (`docs/index.md`)
layout: default title: gRPC-Gateway nav_order: 0 permalink: /
gRPC-Gateway
{: .fs-9 }
gRPC-Gateway is a plugin of protoc. It reads a gRPC service definition and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. This server is generated according to custom options in your gRPC definition.
{: .fs-6 .fw-300 }
Get started{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } View it on GitHub{: .btn .fs-5 .mb-4 .mb-md-0 }
Getting started
gRPC-Gateway helps you to provide your APIs in both gRPC and RESTful style at the same time.
To learn more about gRPC-Gateway check out the documentation.
Contribution
See CONTRIBUTING.md.
License
gRPC-Gateway is licensed under the BSD 3-Clause License.
See LICENSE for more details.
Thank you to the contributors of gRPC-Gateway
{% for contributor in site.github.contributors %}
Docs Contributing Index (`docs/docs/contributing/index.md`)
layout: default title: Contributing nav_order: 5 has_children: true
Docs Development Index (`docs/docs/development/index.md`)
layout: default title: Development nav_order: 4 has_children: true
Docs Mapping Index (`docs/docs/mapping/index.md`)
layout: default title: Mapping nav_order: 2 has_children: true
Docs Operations Index (`docs/docs/operations/index.md`)
layout: default title: Operations nav_order: 3 has_children: true
Docs Overview Index (`docs/docs/overview/index.md`)
layout: default title: Overview nav_order: 1 has_children: true
Docs Tutorials Index (`docs/docs/tutorials/index.md`)
layout: default title: Tutorials nav_order: 6 has_children: true
Docs Tutorials Generating Stubs Index (`docs/docs/tutorials/generating_stubs/index.md`)
layout: default title: Generating stubs parent: Tutorials nav_order: 2 has_children: true
For generating the stubs, we have two alternatives: protoc and buf. protoc is the more classic generation experience that is used widely in the industry, but it has a pretty steep learning curve. buf is a newer tool that is built with user experience and speed in mind. It also offers linting and breaking change detection, something protoc doesn't offer. We offer instructions for both here.
Config.Yml (`docs/_config.yml`)
Site settings
These are used to personalize your new site. If you look in the HTML files,
you will see them accessed via {{ site.title }}, {{ site.github_repo }}, and so on.
You can create any custom variable you would like, and they will be accessible
in the templates via {{ site.myvariable }}.
title: gRPC-Gateway
baseurl: "/grpc-gateway" # the subpath of your site, e.g. /blog
url: "https://grpc-ecosystem.github.io" # the base hostname & protocol for your site, e.g. http://example.com
remote_theme: pmarsceill/[email protected]
permalink: pretty
exclude: ["run.sh"]
Set a path/url to a logo that will be displayed instead of the title
#logo: "/assets/images/grpc-gateway.png"
Enable or disable the site search
Supports true (default) or false
search_enabled: true
search:
Split pages into sections that can be searched individually
Supports 1 - 6, default: 2
heading_level: 2
Maximum amount of previews per search result
Default: 3
previews: 2
Maximum amount of words to display before a matched word in the preview
Default: 5
preview_words_before: 3
Maximum amount of words to display after a matched word in the preview
Default: 10
preview_words_after: 3
Set the search token separator
Default: /[\s\-/]+/
Example: enable support for hyphenated search words
tokenizer_separator: /[\s/]+/
Display the relative url in search results
Supports true (default) or false
rel_url: true
Enable or disable the search button that appears in the bottom right corner of every page
Supports true or false (default)
button: false
Enable or disable heading anchors
heading_anchors: true
Aux links for the upper right navigation
aux_links:
"gRPC-Gateway on GitHub":
- "https://github.com/grpc-ecosystem/grpc-gateway"
Makes Aux links open in a new tab. Default is false
aux_links_new_tab: false
Sort order for navigation links
nav_sort: case_insensitive # default, equivalent to nil
nav_sort: case_sensitive # Capital letters sorted before lowercase
Footer content
appears at the bottom of every page's main content
Back to top link
back_to_top: true
back_to_top_text: "Back to top"
footer_content: 'Copyright © the gRPC-Gateway Authors. Distributed by a BSD 3-Clause License.'
Footer last edited timestamp
last_edit_timestamp: true # show or hide edit time - page must have last_modified_date defined in the frontmatter
last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html
Footer "Edit this page on GitHub" link text
gh_edit_link: true # show or hide edit this page link
gh_edit_link_text: "Edit this page on GitHub"
gh_edit_repository: "https://github.com/grpc-ecosystem/grpc-gateway" # the github URL for your repo
gh_edit_branch: "main" # the branch that your docs is served from
gh_edit_source: docs # the source that your files originate from
gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately
Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define
color_scheme: nil
Disqus Comments
disqus:
Leave shortname blank to disable comments site-wide.
Enable comments for any post by adding `comments: true` to that post's YAML Front Matter.
shortname:
Google Analytics Tracking
e.g, UA-1234567-89
ga_tracking:
ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true/nil by default)
plugins:
- jekyll-seo-tag
- jekyll-include-cache
kramdown:
syntax_highlighter_opts:
block:
line_numbers: false
compress_html:
clippings: all
comments: all
endings: all
startings: []
blanklines: false
profile: false
ignore:
envs: all
Docs Faq (`docs/docs/faq.md`)
layout: default title: FAQ nav_order: 7
FAQ
How can I write the annotations which gRPC-Gateway requires?
The gRPC-Gateway follows the spec of google.api.HttpRule, so first check out the documentation if it is feasible in the spec.
For situations where annotating the proto file is not an option please reference the documentation on gRPC API Configuration
See also a past discussion in the grpc-io mailing list.
I want to support a certain style of HTTP request but the code generated by gRPC-Gateway does not. How can I support this style?
See the question above at first.
The gRPC-Gateway is intended to cover 80% of use cases without forcing you to write comprehensive but complicated annotations. So the gateway itself does not always cover all the use cases you have by design. In other words, the gateway automates typical boring boilerplate mapping between gRPC and HTTP/1 communication, but it does not do arbitrarily complex custom mappings for you.
On the other hand, you can still add whatever you want as a middleware which wrapsruntime.ServeMux. Since runtime.ServeMux is just a standard http.Handler, you can easily write a custom wrapper of runtime.ServeMux, leveraged with existing third-party libraries in Go (e.g. gateway main.go program).
My gRPC server is written in (Scala or C++ or Ruby or Haskell etc). Is there a (Scala or C++ or Ruby or Haskell etc) version of gRPC-Gateway?
As of now, No. But it should not be a big issue because the reverse-proxy which gRPC-Gateway generates usually works as an independent process and communicates with your gRPC server over TCP or a Unix domain sockets (Unix systems only).
Why are the models in the OpenAPI specification prefixed with the last part of the proto package name?
The reason to generate the prefixes is that we don't have a guaranteed unique namespace. If two packages produce different Foo messages then we will have trouble.
Why not strip the prefix?
When a message is added which happens to conflict with another message (e.g. by importing a message with the same name from a different package) it will break code that is very far away from the code that changed. This is in an effort to adhere to the principle of least astonishment.
What is the difference between the gRPC-Gateway and grpc-httpjson-transcoding?
The gRPC-Gateway is a generator that generates a Go implementation of a JSON/HTTP-gRPC reverse proxy based on annotations in your proto file, while the grpc-httpjson-transcoding library doesn't require the generation step, it uses protobuf descriptors as config. It can be used as a component of an existing proxy. Google Cloud Endpoints and the gRPC-JSON transcoder filter in Envoy are using this.
Behavior differences:
- By default, gRPC-Gateway does not escape path parameters in the same way. This can be configured.
What is the difference between the gRPC-Gateway and gRPC-web?
Usage
In the gRPC-Gateway, we generate a reverse-proxy from the proto file annotations. In the front-end, we call directly through REST APIs. We can generate an OpenAPI v2 specification that may further be used to generate the frontend client from using protoc-gen-openapiv2.
In gRPC-web, the client code is generated directly from the proto files and can be used in the frontend.
Performance
The gRPC-Gateway parses JSON to the protobuf binary format before sending it to the gRPC server. It then has to parse the reply back from the protobuf binary format to JSON again The parsing overhead has a negative impact on performance.
In gRPC-web, the message is sent in the protobuf binary format already, so there is no additional parsing cost on the proxy side.
Maintenance
With the gRPC-Gateway, if your proto file changes, we have to regenerate the gateway reverse proxy code. If you are using the HTTP/JSON interface you probably have to change the front-end too, which means making changes in two places.
In gRPC-web, regenerating the files from the proto file will automatically update the front-end client.
Docs Related Projects (`docs/docs/related_projects.md`)
layout: default title: Related projects nav_order: 8
Related projects
-
A dynamically configured alternative to the grpc-gateway written in Node.
-
A statically configured alternative to the grpc-gateway written in Node.
The Envoy proxy gRPC-JSON transcoder
An Envoy proxy filter that translates incoming JSON requests to gRPC and back.
Google Cloud Platform HTTP/JSON gRPC transcoding
A GCP product that behaves like the grpc-gateway.
gRPC gateway project generator gen-grpc-gateway-api
A shell script that can be used to generate a Go project that utilizes the gRPC-gateway and protobuf to expose a simple API.
Tutorial guide: Automate gRPC-Gateway project generation in Go with gen-grpc-gateway-api
-
A Java implementation of grpc-gateway based on Spring Boot.
-
An example repo showcasing how to use gRPC-Gateway with a Java Maven multi-module project.
Docs Using Custom Query Parser (`docs/docs/using_custom_query_parser.md`)
Custom Query Parameter Parsing in gRPC-Gateway
The gRPC-Gateway allows you to customize the way query parameters are parsed and mapped to your gRPC request messages. This can be particularly useful when dealing with nested fields or complex query parameter mappings that are not directly supported by the default behavior.
Example: Custom Query Parameter Parser for Nested Fields
Suppose you have a gRPC service definition where you want to map query parameters to a nested message field. Here is how you can achieve this using a custom query parameter parser.
Protobuf Definition:
syntax = "proto3";
import "google/api/annotations.proto";
package your.service.v1;
message PageOptions {
int32 limit = 1;
int32 page = 2;
}
message ListStuffRequest {
string stuff_uuid = 1;
PageOptions pagination = 2;
}
message ListStuffResponse {
repeated string stuff = 1;
}
service MyService {
rpc ListStuff(ListStuffRequest) returns (ListStuffResponse) {
option (google.api.http) = {
get: "/path/to/{stuff_uuid}/stuff"
};
}
}The Problem
The ListStuffRequest message contains a nested PageOptions message that represents pagination options for the list operation. By default, creating a ServerMux in gRPC-Gateway will be able to parse query parameters for the stuff_uuid and other basic fields (that are not your own custom message). Given that PageOptions is a custom message, and therefore will lead to a nested RequestMessage (the root message), it can only parse query parameters for the PageOptions message if they are in the format url?pagination.limit=x. If your endpoint constraints require query parameters to be in the format url?limit=x, without specifying the full qualified path in the url, you will need to implement a custom parser to map these parameters correctly into the nested PageOptions message.
Custom Query Parameter Parser
Create a custom QueryParameterParser to handle the nested PageOptions message.
package customparser
import (
"net/url"
"strconv"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/protobuf/proto"
your_service_v1 "path/to/your/service/v1"
)
// CustomQueryParameterParser parses query parameters into the appropriate gRPC message fields.
type CustomQueryParameterParser struct{}
// Parse parses query parameters and populates the appropriate fields in the gRPC request message.
func (p *CustomQueryParameterParser) Parse(target proto.Message, values url.Values, filter *utilities.DoubleArray) error {
switch req := target.(type) {
// Different messages/requests can have different parsers, of course
case *your_service_v1.ListStuffRequest:
return populateListStuffParams(values, req)
}
return (runtime.DefaultQueryParser{}).Parse(target, values, filter)
}
// populateListStuffParams populates the ListStuffRequest with query parameters.
func populateListStuffParams(values url.Values, r *your_service_v1.ListStuffRequest) error {
pageOptions := &your_service_v1.PageOptions{}
if limit := values.Get("limit"); limit != "" {
if parsedLimit, err := strconv.Atoi(limit); err == nil {
pageOptions.Limit = int32(parsedLimit)
}
}
if page := values.Get("page"); page != "" {
if parsedPage, err := strconv.Atoi(page); err == nil {
pageOptions.Page = int32(parsedPage)
}
}
r.Pagination = pageOptions
return nil
}Integrate Your Custom Query Parameter Parser in gRPC-Gateway Setup
All you need to do now is update the gRPC-Gateway setup, particularly wherever you are defining the mux server, to use the custom query parameter parser.
package main
import (
"context"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/encoding/protojson"
"your_module/path/customparser"
)
// create a new ServeMux with custom parser and other runtime options as needed
func createGRPCGatewayMux() *runtime.ServeMux {
// whatever custom code you may need before you create the mux...
return runtime.NewServeMux(
// Custom query parameter parser
runtime.SetQueryParameterParser(&customparser.CustomQueryParameterParser{}),
// other runtime options you may need...
)
}Docs Contributing 2020 Season Of Docs (`docs/docs/contributing/2020_season_of_docs.md`)
layout: default title: Google Season of Docs nav_order: 1 parent: Contributing
2020 Season of Docs
The gRPC-Gateway participated in the 2020 Google Season of Docs.
The project was completed by @iamrajiv. A summary of the project
work can be found in the
project report.
Docs Contributing Getting Started (`docs/docs/contributing/getting_started.md`)
layout: default title: Getting started nav_order: 0 parent: Contributing
How to contribute
See CONTRIBUTING.md.
Docs Development Grpc Gateway V2 Migration Guide (`docs/docs/development/grpc-gateway_v2_migration_guide.md`)
layout: default title: gRPC-Gateway v2 migration guide nav_order: 0 parent: Development
gRPC-Gateway v2 migration guide
This guide is supposed to help users of the gateway migrate from v1 to v2. See the original issue for detailed information on all changes that were made specifically to v2.
The following behavioural defaults have been changed:
protoc-gen-swagger has been renamed protoc-gen-openapiv2
See the original issue
for more information. Apart from the new name, the only real difference to users will be a slightly different proto annotation:
import "protoc-gen-openapiv2/options/annotations.proto";instead of
import "protoc-gen-swagger/options/annotations.proto";and
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {instead of
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {The Bazel rule has been renamed protoc_gen_openapiv2.
The example field in the OpenAPI annotations is now a string
This was a google.protobuf.Any type, but it was only used for the JSON representation, and it was breaking some tools and it was generally unclear to the user how it works. It is now a string instead. The value is copied verbatim to the output OpenAPI file. Remember to escape any quotes in the strings.
For example, if you had an example that looked like this:
example: { value: '{ "uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814" }' }It would now look like this:
example: "{\"uuid\": \"0cf361e1-4b44-483d-a159-54dabdf7e814\"}"See a_bit_of_everything.proto in the example protos for more examples.
We now use the camelCase JSON names by default
See the original issue and
original pull request for more information.
If you want to revert to the old behaviour, configure a custom marshaler with UseProtoNames: true:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
Marshaler: &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
},
}),
)To change the OpenAPI generator behaviour to match, set json_names_for_fields=false when generating:
--openapiv2_out=json_names_for_fields=false:./gen/openapiv2 path/to/my/proto/v1/myproto.protoIf using the Bazel rule, set json_names_for_fields=False.
We now emit default values for all fields
See the original issue
for more information.
If you want to revert to the old behaviour, configure a custom marshaler withEmitUnpopulated: false:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
Marshaler: &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
EmitUnpopulated: false,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
},
}),
)We now support google.api.HttpBody message types by default
The runtime.SetHTTPBodyMarshaler function has disappeared, and is now
enabled by default. If you for some reason don't want HttpBody messages to be
respected, you can disable it by overwriting the default marshaler with one which
does not wrap runtime.JSONPb in runtime.HTTPBodyMarshaler:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
}),
)runtime.DisallowUnknownFields has been removed
All marshalling settings are now inherited from the configured marshaler. If you wish
to disallow unknown fields, configure a custom marshaler:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
Marshaler: &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: false,
},
},
}),
)WithLastMatchWins and allow_colon_final_segments=true is now default behaviour
If you were previously specifying these, please remove them, as this is now
the default behaviour. See the original issue for more information.
There is no workaround for this, as we considered it a correct interpretation of the spec. If this breaks your application, carefully consider the order in which you define your services.
Error handling configuration has been overhauled
runtime.HTTPError, runtime.OtherErrorHandler, runtime.GlobalHTTPErrorHandler, runtime.WithProtoErrorHandler are all gone. Error handling is rewritten around the use of gRPCs Status types. If you wish to configure how the gateway handles errors, please use runtime.WithErrorHandler and runtime.WithStreamErrorHandler. To handle routing errors (similar to the removed runtime.OtherErrorHandler) please use runtime.WithRoutingErrorHandler.
Default query parameter parsing behaviour change
The default behaviour for query parameter parsing has changed to return an InvalidArgument (400 Bad Request) error when more than one of the same matching query parameters is parsed. Previously, it would log but not return an error, using the first query parameter that matched and ignoring any others. See the original issue for more information.
Docs Development Installation For Cygwin (`docs/docs/development/installation_for_cygwin.md`)
layout: default title: Installation for Cygwin nav_order: 1 parent: Development
Installation for Cygwin
Installation
First, you need to install the Go language. Please install the latest version, not the one that is listed here.
wget -N https://storage.googleapis.com/golang/go1.8.1.windows-amd64.msi
msiexec /i go1.8.1.windows-amd64.msi /passive /promptrestartThen you need to install ProtocolBuffers 3.0.0-beta-3 or later. Use the Windows release as no native Cygwin protoc with version 3 is available yet.
wget -N https://github.com/google/protobuf/releases/download/v3.2.0/protoc-3.2.0-win32.zip`
7z x protoc-3.2.0-win32.zip -o/usr/local/Then you need to set up your Go workspace. Create the workspace dir.
mkdir /home/user/go
mkdir /home/user/go/bin
mkdir /home/user/go/pkg
mkdir /home/user/go/srcFrom an elevated cmd.exe prompt set the GOPATH variable in Windows and add the $GOPATH/bin directory to your path using reg add instead of setx because setx can truncate your PATH variable to 1024 characters.
setx GOPATH c:\path\to\your\cygwin\home\user\go /M
set pathkey="HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment"
for /F "usebackq skip=2 tokens=2*" %A IN (`reg query %pathkey% /v Path`) do (reg add %pathkey% /f /v Path /t REG_SZ /d "%B;c:\path\to\your\cygwin\home\user\go\bin")Then go get -u -v the following packages:
go get -u -v github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go get -u -v github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
go get -u -v google.golang.org/protobuf/cmd/protoc-gen-go
go get -u -v google.golang.org/grpc/cmd/protoc-gen-go-grpcThis will probably fail with a similar output to this:
github.com/grpc-ecosystem/grpc-gateway (download)
# cd .; git clone https://github.com/grpc-ecosystem/grpc-gateway C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway
Cloning into 'C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway'...
fatal: Invalid path '/home/user/go/C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway': No such file or directory
package github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway: exit status 128To fix this you need to run the go get -u -v commands and look for all lines starting with # cd .;. Copy and paste these lines into your shell and change the clone destination directories.
git clone https://github.com/grpc-ecosystem/grpc-gateway $(cygpath -u $GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway
git clone https://github.com/golang/protobuf $(cygpath -u $GOPATH)/src/github.com/golang/protobuf
git clone https://github.com/google/go-genproto $(cygpath -u $GOPATH)/src/google.golang.org/genprotoOnce the clone operations are finished the go get -u -v commands shouldn't give you an error anymore.
Usage
Follow the instructions in the README.
Adjust steps 3, 5 and 7 like this. protoc expects native Windows paths.
protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src --go_out=. --go-grpc_out=. ./path/to/your_service.proto
protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src ./path/to/your_service.proto
protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src ./path/to/your_service.protoThen cd into the directory where your entry-point main.go file is located and run:
go get -vThis will fail in this same way as it did during the installation. Look for all lines starting with # cd .;. Copy and paste these lines into your shell and change the clone destination directories.
git clone https://go.googlesource.com/net $(cygpath -u $GOPATH)/src/golang.org/x/net
git clone https://go.googlesource.com/text $(cygpath -u $GOPATH)/src/golang.org/x/text
git clone https://github.com/grpc/grpc-go $(cygpath -u $GOPATH)/src/google.golang.org/grpcOnce the clone operations are finished the go get -v commands shouldn't give you an error anymore.
Then run:
go installThis will compile and install your gRPC-Gateway service into $GOPATH/bin.
Docs Mapping Binary File Uploads (`docs/docs/mapping/binary_file_uploads.md`)
layout: default title: Binary file uploads nav_order: 2 parent: Mapping
Binary file uploads
If you need to do a binary file upload, e.g. via;
curl -X POST -F "attachment=@/tmp/somefile.txt" http://localhost:9090/v1/filesthen your request will contain the binary data directly and there is no way to model this using gRPC.
What you can do instead is to add a custom route directly on the mux instance.
Custom route on a mux instance
Here we'll setup a handler (handleBinaryFileUpload) for POST requests:
// Create a mux instance
mux := runtime.NewServeMux()
// Attachment upload from http/s handled manually
mux.HandlePath("POST", "/v1/files", handleBinaryFileUpload)And then in your handler you can do something like:
func handleBinaryFileUpload(w http.ResponseWriter, r *http.Request, params map[string]string) {
err := r.ParseForm()
if err != nil {
http.Error(w, fmt.Sprintf("failed to parse form: %s", err.Error()), http.StatusBadRequest)
return
}
f, header, err := r.FormFile("attachment")
if err != nil {
http.Error(w, fmt.Sprintf("failed to get file 'attachment': %s", err.Error()), http.StatusBadRequest)
return
}
defer f.Close()
//
// Now do something with the io.Reader in `f`, i.e. read it into a buffer or stream it to a gRPC client side stream.
// Also `header` will contain the filename, size etc of the original file.
//
}Docs Mapping Custom Marshalers (`docs/docs/mapping/custom_marshalers.md`)
layout: default title: Custom marshalers nav_order: 6 parent: Mapping
Custom marshalers
Marshaler
implementations can implement optional additional methods to customize their
behaviour beyond the methods required by the core interface.
Stream delimiters
By default, a streamed response delimits each response body with a single
newline ("\n"). You can change this delimiter by having your marshaler
implementDelimited.
For example, to separate each entry with a pipe ("|") instead:
type YourMarshaler struct {
// ...
}
// ...
func (*YourMarshaler) Delimiter() []byte {
return []byte("|")
}Stream content type
By default, a streamed response emits a Content-Type header that is the same
for a unary response, from the ContentType() method of theMarshaler
interface.
If you require the server to declare a distinct content type for stream
responses versus unary responses, the marshaler must implementStreamContentType.
This provides the MIME type when specifically responding to a streaming
response.
For example, by default theJSONPb
marshaler results in application/json for its Content-Type response header,
irrespective of unary versus streaming. This can be changed for streaming
endpoints by wrapping the marshaler with a custom marshaler that implementsStreamContentType
to return the NDJSON MIME type for
streaming response endpoints:
type CustomJSONPb struct {
runtime.JSONPb
}
func (*CustomJSONPb) Delimiter() []byte {
// Strictly speaking this is already the default delimiter for JSONPb, but
// providing it here for completeness with an NDJSON marshaler all in one
// place.
return []byte("\n")
}
func (*CustomJSONPb) StreamContentType(interface{}) string {
return "application/x-ndjson"
}Docs Mapping Customizing Openapi Output (`docs/docs/mapping/customizing_openapi_output.md`)
layout: default title: Customizing OpenAPI Output nav_order: 4 parent: Mapping
{% raw %}
Customizing OpenAPI Output
In proto comments
You can provide comments directly in your Protocol Buffer definitions and they will be translated into comments in the generated OpenAPI definitions:
message MyMessage {
// This comment will end up directly in your Open API definition
string uuid = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The UUID field."}];
}You can disable this behavior and exclude all protobuf comments from OpenAPI output via the ignore_comments option.
Using proto options
You can define options on your Protocol Buffer services, operations, messages, enums and field definitions to customize your Open API output. For instance, to customize the OpenAPI Schema Object for messages and fields:
import "protoc-gen-openapiv2/options/annotations.proto";
message ABitOfEverything {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
json_schema: {
title: "A bit of everything"
description: "Intentionally complicated message type to cover many features of Protobuf."
required: ["uuid", "int64_value", "double_value"]
}
external_docs: {
url: "https://github.com/grpc-ecosystem/grpc-gateway";
description: "Find out more about ABitOfEverything";
}
example: "{\"uuid\": \"0cf361e1-4b44-483d-a159-54dabdf7e814\"}"
extensions: {
key: "x-irreversible";
value {
bool_value: true;
}
}
};
string uuid = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The UUID field."}];
}Enums can be customized like messages:
// NumericEnum is one or zero.
enum NumericEnum {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_enum) = {
description: "NumericEnum is one or zero."
title: "NumericEnum"
extensions: {
key: "x-a-bit-of-everything-foo"
value {
string_value: "bar"
}
}
external_docs: {
url: "https://github.com/grpc-ecosystem/grpc-gateway"
description: "Find out more about ABitOfEverything"
}
example: "\"ZERO\""
};
// ZERO means 0
ZERO = 0;
// ONE means 1
ONE = 1;
}Operations can also be customized:
service ABitOfEverythingService {
rpc Delete(grpc.gateway.examples.internal.proto.sub2.IdMessage) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/v1/example/a_bit_of_everything/{uuid}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
security: {
security_requirement: {
key: "ApiKeyAuth";
value: {}
}
security_requirement: {
key: "OAuth2";
value: {
scope: "read";
scope: "write";
}
}
}
extensions: {
key: "x-irreversible";
value {
bool_value: true;
}
}
};
}
}Swagger Extensions can be added as key-value pairs to the options. Keys must begin with x- and values can be of any type listed here. For example:
extensions: {
key: "x-amazon-apigateway-authorizer";
value {
struct_value {
fields {
key: "type";
value {
string_value: "token";
}
}
fields {
key: "authorizerResultTtlInSeconds";
value {
number_value: 60;
}
}
}
}
}Please see this a_bit_of_everything.proto for examples of the options being used.
Using google.api.field_behavior
Google provides a field option for defining the behavior of fields that is also supported:
import "google/api/field_behavior.proto";
message MyMessage {
string a_required_field = 1 [(google.api.field_behavior) = REQUIRED];
}The following options are used in the Open API output:
REQUIRED- marks a field as requiredOUTPUT_ONLY- marks a field as readonly
Google defines a couple of other options - OPTIONAL, IMMUTABLE, INPUT_ONLY -
that are not currently used. OPTIONAL support is currently under discussion
in this issue.
For IMMUTABLE and INPUT_ONLY fields, there is an open issue in the Open API specification for adding functionality for write-once or immutable fields to the spec.
Using go templates in proto file comments
Use Go templates in your proto file comments to allow more advanced documentation such as:
- Documentation about fields in the proto objects.
- Import the content of external files (such as
Markdown).
How to use it
By default this function is turned off, so if you want to use it you have to add the use_go_templates option:
--openapiv2_out . --openapiv2_opt use_go_templates=trueor:
--openapiv2_out=use_go_templates=true:.Example script
Example of a bash script with the use_go_templates flag set to true:
$ protoc -I. \
--go_out . --go-grpc_out . \
--grpc-gateway_out . \
--openapiv2_out . \
--openapiv2_opt use_go_templates=true \
path/to/my/proto/v1/myproto.protoExample proto file
Example of a proto file with Go templates. This proto file imports documentation from another file, tables.md:
service LoginService {
// Login
//
// {{.MethodDescriptorProto.Name}} is a call with the method(s) {{$first := true}}{{range .Bindings}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{.HTTPMethod}}{{end}} within the "{{.Service.Name}}" service.
// It takes in "{{.RequestType.Name}}" and returns a "{{.ResponseType.Name}}".
//
// {{import "tables.md"}}
rpc Login (LoginRequest) returns (LoginReply) {
option (google.api.http) = {
post: "/v1/example/login"
body: "*"
};
}
}
message LoginRequest {
// The entered username
string username = 1;
// The entered password
string password = 2;
}
message LoginReply {
// Whether you have access or not
bool access = 1;
}The content of tables.md:
## {{.RequestType.Name}}
| Field ID | Name | Type | Description |
| ----------- | --------- | --------------------------------------------------------- | ---------------------------- | {{range .RequestType.Fields}}
| {{.Number}} | {{.Name}} | {{if eq .Label.String "LABEL_REPEATED"}}[]{{end}}{{.Type}} | {{fieldcomments .Message .}} | {{end}}
## {{.ResponseType.Name}}
| Field ID | Name | Type | Description |
| ----------- | --------- | ---------------------------------------------------------- | ---------------------------- | {{range .ResponseType.Fields}}
| {{.Number}} | {{.Name}} | {{if eq .Label.String "LABEL_REPEATED"}}[]{{end}}{{.Type}} | {{fieldcomments .Message .}} | {{end}}OpenAPI output
SwaggerUI
This is how the OpenAPI file would be rendered in Swagger UI.
Postman
This is how the OpenAPI file would be rendered in Postman.
For a more detailed example of a proto file that has Go, templates enabled, see the examples.
Using custom values
Custom values can be specified in the Go templates that generate your proto file comments.
A use case might be to interpolate different external documentation URLs when rendering documentation for different environments.
How to use it
The use_go_templates option has to be enabled as a prerequisite.
Provide customized values in the format of go_template_args=my_key=my_value. {{arg "my_key"}} will be replaced with my_value in the Go template.
Specify the go_template_args option multiple times if needed.
--openapiv2_out . --openapiv2_opt use_go_templates=true --openapiv2_opt go_template_args=my_key1=my_value1 --openapiv2_opt go_template_args=my_key2=my_value2
...Example script
Example of a bash script with the use_go_templates flag set to true and custom template values set:
$ protoc -I. \
--go_out . --go-grpc_out . \
--grpc-gateway_out . \
--openapiv2_out . \
--openapiv2_opt use_go_templates=true \
--openapiv2_opt go_template_args=environment=test1 \
--openapiv2_opt go_template_args=environment_label=Test1 \
path/to/my/proto/v1/myproto.protoExample proto file
Example of a proto file with Go templates and custom values:
service LoginService {
// Login (Environment: {{arg "environment_label"}})
//
// {{.MethodDescriptorProto.Name}} is a call with the method(s) {{$first := true}}{{range .Bindings}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{.HTTPMethod}}{{end}} within the "{{.Service.Name}}" service.
// It takes in "{{.RequestType.Name}}" and returns a "{{.ResponseType.Name}}".
// This only works in the {{arg "environment"}} domain.
//
rpc Login (LoginRequest) returns (LoginReply) {
option (google.api.http) = {
post: "/v1/example/login"
body: "*"
};
}
}Other plugin options
A comprehensive list of OpenAPI plugin options can be found here. Options can be passed via protoc CLI:
--openapiv2_out . --openapiv2_opt bar=baz,color=redOr, with buf in buf.gen.yaml:
- name: openapiv2
out: foo
opt: bar=baz,color=redMerging output
If your protobuf definitions are spread across multiple files, the OpenAPI plugin will create a file for each .proto input. This may make sense for Go bindings, since they still share a package space, but fragmenting OpenAPI specifications across multiple files changes the schema itself.
To merge disparate .proto inputs into a single OpenAPI file, use the allow_merge and merge_file_name options.
opt: allow_merge=true,merge_file_name=foo will result in a single foo.swagger.json. Note that you may need to set
the generation strategy to all when merging many files:
- name: openapiv2
out: foo
strategy: all
opt: allow_merge=true,merge_file_name=fooFile-level options of the merged output (such as info, host or tags) are taken from the first input file that has
an openapiv2_swagger option, either as an annotation or in the openapi_configuration file. File-level options set
on any other input file are ignored.
Enums as integers
To generate enums as integers instead of strings, use enums_as_ints.
opt: enums_as_ints=true will result in:
{
"name": "enumValue",
"description": " - Example enums",
"in": "query",
"required": false,
"type": "int",
"enum": [
0,
1
],
"default": 0
},Omitting the default value of enums
If you define enum types with non default value such as declaring 0 value with UNKNOWN and want to omit the default value from generated swagger file, use omit_enum_default_value.
This option also applies if enums_as_ints option is enalbled to generate enums as integer.
opt: omit_enum_default_value=true will result in:
Input Example:
enum enumValue {
UNKNOWN = 0;
FOO = 1;
}Output json:
{
"name": "enumValue",
"description": " - Example enums",
"in": "query",
"required": false,
"type": "string",
"enum": [
"FOO"
]
},Hiding fields, methods, services and enum values
If you require internal or unreleased fields and APIs to be hidden from your API documentation, google.api.VisibilityRule annotations can be added to customize where they are generated. Combined with the option visibility_restriction_selectors, overlapping rules will appear in the OpenAPI output.
visibility_restriction_selectors can be declared multiple times as an option to include multiple visibility restrictions in the output.
e.g. if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- visibility_restriction_selectors=PREVIEW
- visibility_restriction_selectors=INTERNALor with protoc
protoc --openapiv2_out=. --openapiv2_opt=visibility_restriction_selectors=PREVIEW --openapiv2_opt=visibility_restriction_selectors=INTERNAL ./path/to/file.protoElements without google.api.VisibilityRule annotations will appear as usual in the generated output.
These restrictions and selectors are completely arbitrary and you can define whatever values or hierarchies you want. In this example we use INTERNAL and PREVIEW, but INTERNAL, ALPHA, BETA, RELEASED, or anything else could be used if you wish.
Note: Annotations are only supported on Services, Methods, Fields and Enum Values.
opt: visibility_restriction_selectors=PREVIEW will result in:
Input Example:
service Echo {
rpc EchoInternal(VisibilityRuleSimpleMessage) returns (VisibilityRuleSimpleMessage) {
option (google.api.method_visibility).restriction = "INTERNAL";
option (google.api.http) = {
get: "/v1/example/echo_internal"
};
}
rpc EchoInternalAndPreview(VisibilityRuleSimpleMessage) returns (VisibilityRuleSimpleMessage) {
option (google.api.method_visibility).restriction = "INTERNAL,PREVIEW";
option (google.api.http) = {
get: "/v1/example/echo_internal_and_preview"
};
}
}
message VisibilityRuleSimpleMessage {
enum VisibilityEnum {
UNSPECIFIED = 0;
VISIBLE = 1;
INTERNAL = 2 [(google.api.value_visibility).restriction = "INTERNAL"];
PREVIEW = 3 [(google.api.value_visibility).restriction = "INTERNAL,PREVIEW"];
}
string internal_field = 1 [(google.api.field_visibility).restriction = "INTERNAL"];
string preview_field = 2 [(google.api.field_visibility).restriction = "INTERNAL,PREVIEW"];
VisibilityEnum an_enum = 3;
}Output json:
/* Detailed source-code truncated for AI context efficiency. */For a more in depth example see visibility_rule_echo_service.proto and the following output files for different values of visibility_restriction_selectors:
visibility_restriction_selectors=PREVIEWvisibility_restriction_selectors=INTERNALvisibility_restriction_selectors=INTERNAL,visibility_restriction_selectors=PREVIEW- Not set
Path parameters
When defining HTTP bindings with path parameters that contain multiple path segments, as suggested by the Google AIPs, the path parameter names are numbered to avoid generating duplicate paths in the OpenAPI file.
For example, consider:
service LibraryService {
rpc GetShelf(GetShelfRequest) returns (Shelf) {
option (google.api.http) = {
get: "/v1/{name=shelves/*}"
};
}
rpc GetBook(GetBookRequest) returns (Book) {
option (google.api.http) = {
get: "/v1/{name=shelves/*/books/*}"
};
}
}
message GetShelfRequest {
string name = 1;
}
message GetBookRequest {
string name = 1;
}This will generate the following paths:
/v1/{name}/v1/{name_1}
To override the path parameter names, annotate the field used as path parameter:
message GetShelfRequest {
string name = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {field_configuration: {path_param_name: "shelfName"}}];
}
message GetBookRequest {
string name = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {field_configuration: {path_param_name: "bookName"}}];
}This will instead generate the following paths:
/v1/{shelfName}/v1/{bookName}
Note that path parameters in OpenAPI does not support values with /, as discussed in
Support for path parameters which can contain slashes #892,
so tools as Swagger UI will URL encode any / provided as parameter value. A possible workaround for this is to write
a custom post processor for your OAS file to replace any path parameter with / into multiple parameters.
Expand path parameters containing sub-path segments
Alternative to the above, you can enable the expand_slashed_path_patterns compiler option to expand path parameters containing sub-path segments into the URI.
For example, consider:
rpc GetBook(GetBookRequest) returns (Book) {
option (google.api.http) = {
get: "/v1/{name=publishers/*/books/*}"
};
}Where the GetBook has a path parameter name with a pattern publishers/*/books/*. When you enable the expand_slashed_path_patterns=true option the path pattern is expanded into the URI and each wildcard in the pattern is transformed into new path parameter. The generated schema for previous protobuf is:
{
"/v1/publishers/{publisher}/books/{book}": {
"get": {
"parameters": [
{
"name": "publisher",
"in": "path",
"required": true,
"type": "string",
},
{
"name": "book",
"in": "path",
"required": true,
"type": "string",
}
]
}
}
}The URI is now pretty descriptive and there are two path parameters publisher and book instead of one name. The name of the new parameters is derived from the path segment before the wildcard in the pattern.
Caveats:
- the fact that the original
nameparameter is missing might complicate the usage of the API if you intend to pass in thenameparameters from the resources, - when the
expand_slashed_path_patternscompiler flag is enabled, thepath_param_namefield annotation is ignored.
Output format
By default the output format is JSON, but it is possible to configure it using the output_format option. Allowed values are: json, yaml. The output format will also change the extension of the output files.
For example, if using buf:
- name: openapiv2
out: pkg
opt: output_format=yamlInput example:
syntax = "proto3";
package helloproto.v1;
option go_package = "helloproto/v1;helloproto";
import "google/api/annotations.proto";
service EchoService {
rpc Hello(HelloReq) returns (HelloResp) {
option (google.api.http) = {
get: "/api/hello"
};
}
}
message HelloReq {
string name = 1;
}
message HelloResp {
string message = 1;
}Output:
swagger: "2.0"
info:
title: helloproto/v1/example.proto
version: version not set
tags:
- name: EchoService
consumes:
- application/json
produces:
- application/json
paths:
/api/hello:
get:
operationId: EchoService_Hello
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1HelloResp'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/rpcStatus'
parameters:
- name: name
in: query
required: false
type: string
tags:
- EchoService
definitions:
protobufAny:
type: object
properties:
'@type':
type: string
additionalProperties: {}
rpcStatus:
type: object
properties:
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
$ref: '#/definitions/protobufAny'
v1HelloResp:
type: object
properties:
message:
type: stringDisable service tag generation
By default service tags are generated for backend services, but it is possible to disable it using the disable_service_tags option. Allowed values are: true, false.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- disable_service_tags=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=disable_service_tags=true ./path/to/file.protoInput example:
syntax = "proto3";
package helloproto.v1;
option go_package = "helloproto/v1;helloproto";
import "google/api/annotations.proto";
service EchoService {
rpc Hello(HelloReq) returns (HelloResp) {
option (google.api.http) = {
get: "/api/hello"
};
}
}
message HelloReq {
string name = 1;
}
message HelloResp {
string message = 1;
}Output (tags object are not generated):
swagger: "2.0"
info:
title: helloproto/v1/example.proto
version: version not set
consumes:
- application/json
produces:
- application/json
paths:
/api/hello:
get:
operationId: EchoService_HelloAllow proto files without a go_package option
By default, protoc-gen-openapiv2 requires every proto file to declare a go_package option, even though the generator never emits Go source and has no real use for the value. Set ignore_go_package_option=true to tolerate proto files that don't declare one — a placeholder import path is synthesized instead of erroring. Note: if generate_x_go_type is also enabled, the synthesized placeholder (not a real Go import path) will appear in the generated x-go-type extension for any file that used this fallback.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- ignore_go_package_option=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=ignore_go_package_option=true ./path/to/file.protoDisable default responses
By default a 200 OK response is rendered for each service operation. But it is possible to disable this and explicitly define your service's responses, using the disable_default_responses option. Allowed values are: true, false.
Note: This does not alter the behavior of the gateway itself and should be coupled with a ForwardResponseWriter when altering status codes, see Controlling HTTP Response Codes.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- disable_default_responses=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=disable_default_responses=true ./path/to/file.protoInput example:
syntax = "proto3";
package helloproto.v1;
import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
option go_package = "helloproto/v1;helloproto";
service EchoService {
rpc Hello(HelloReq) returns (HelloResp) {
option (google.api.http) = {get: "/api/hello"};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "201",
value: {
description: "Created";
schema: {
json_schema: {ref: ".helloproto.v1.HelloResp"}
}
}
};
};
}
}
message HelloReq {
string name = 1;
}
message HelloResp {
string message = 1;
}Output (default response not generated):
swagger: "2.0"
info:
title: helloproto/v1/hello.proto
version: version not set
consumes:
- application/json
produces:
- application/json
paths:
/api/hello:
get:
operationId: EchoService_Hello
responses:
"201":
description: Created
schema:
$ref: "#/definitions/v1HelloResp"
parameters:
- name: name
in: query
required: false
type: string
definitions:
v1HelloResp:
type: object
properties:
message:
type: stringOmit array item type when $ref is a sibling
By default, when generating OpenAPI schemas for array items that reference other definitions, both type: object and $ref properties are included. However, some strict OpenAPI validators enforce the no-$ref-siblings rule, which prohibits having $ref alongside other properties at the same level. To comply with this strict validation, you can use the omit_array_item_type_when_ref_sibling option. When enabled, this option omits the type: object property from array items when a $ref is present, since $ref already implies the type. Allowed values are: true, false.
Note: This option only affects the generated OpenAPI specification and does not alter the behavior of the gateway itself.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- omit_array_item_type_when_ref_sibling=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=omit_array_item_type_when_ref_sibling=true ./path/to/file.protoInput example:
syntax = "proto3";
package example.v1;
import "google/api/annotations.proto";
option go_package = "example/v1;example";
service ExampleService {
rpc GetItems(GetItemsRequest) returns (GetItemsResponse) {
option (google.api.http) = {get: "/api/items"};
}
}
message GetItemsRequest {}
message GetItemsResponse {
repeated Item items = 1;
}
message Item {
string id = 1;
string name = 2;
}Output without the option (default behavior):
definitions:
v1GetItemsResponse:
type: object
properties:
items:
type: array
items:
type: object # This can violate strict no-$ref-siblings rule
$ref: "#/definitions/v1Item"
v1Item:
type: object
properties:
id:
type: string
name:
type: stringOutput with omit_array_item_type_when_ref_sibling=true:
definitions:
v1GetItemsResponse:
type: object
properties:
items:
type: array
items:
$ref: "#/definitions/v1Item" # type: object is omitted
v1Item:
type: object
properties:
id:
type: string
name:
type: stringCustom HTTP Header Request Parameters
By default the parameters for each operation are generated from the protocol buffer definition however you can extend the parameters to include extra HTTP headers if required.
NOTE: These annotations do not alter the behaviour of the gateway and must be coupled with custom header parsing behaviour in the application. Also be aware that adding header parameters can alter the forwards and backwards compatibility of the schema. You must also set a type for your header which can be one of STRING, INTEGER, NUMBER or BOOLEAN.
syntax = "proto3";
package helloproto.v1;
import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
option go_package = "helloproto/v1;helloproto";
service EchoService {
rpc Hello(HelloReq) returns (HelloResp) {
option (google.api.http) = {get: "/api/hello"};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
parameters: {
headers: {
name: "X-Foo";
description: "Foo Header";
type: STRING,
required: true;
};
headers: {
name: "X-Bar";
description: "Bar Header";
type: NUMBER,
};
};
};
}
}
message HelloReq {
string name = 1;
}
message HelloResp {
string message = 1;
}Output:
swagger: "2.0"
info:
title: helloproto/v1/hello.proto
version: version not set
consumes:
- application/json
produces:
- application/json
paths:
/api/hello:
get:
operationId: Hello
responses:
"200":
description: A successful response.
schema:
$ref: "#/definitions/helloproto.v1.HelloResp"
parameters:
- name: name
in: query
required: false
type: string
- name: X-Foo
description: Foo Header
in: header
required: true
type: string
- name: X-Bar
description: Bar Header
in: header
required: false
type: number
definitions:
helloproto.v1.HelloResp:
type: object
properties:
message:
type: stringIgnore comments
If you want to exclude all protobuf comments (such as // buf:lint:ignore) from OpenAPI output, use the ignore_comments option. Allowed values are: true, false.
Note: ignore_comments and use_go_templates are mutually exclusive and cannot be enabled at the same time.
If you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- ignore_comments=trueor with protoc:
protoc --openapiv2_out=. --openapiv2_opt=ignore_comments=true ./path/to/file.protoRemoving internal comments
If you want to remove internal comments from the from OpenAPI output (such as TODO and FIXME directives) you can use the remove_internal_comments option.
If set to true, this will remove all comment text located between (-- and --) as per AIP 192: Internal comments.
Preserve RPC Path Order
By default, generated Swagger files emit paths found in proto files in alphabetical order. If you would like to
preserve the order of emitted paths to mirror the path order found in proto files, you can use the preserve_rpc_order option. If set to true, this option will ensure path ordering is preserved for Swagger files with both json and yaml formats.
This option will also ensure path ordering is preserved in the following scenarios:
- When using additional bindings, paths will preserve their ordering within an RPC.
- When using multiple services, paths will preserve their ordering between RPCs in the whole protobuf file.
- When merging protobuf files, paths will preserve their ordering depending on the order of files specified on the command line.
preserve_rpc_order can be passed via the protoc CLI:
protoc --openapiv2_out=. --openapiv2_opt=preserve_rpc_order=true ./path/to/file.protoOr, with buf in buf.gen.yaml:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- preserve_rpc_order=trueEnable RPC deprecation
With enable_rpc_deprecation option you can deprecate openapi method using standard method's option. Allowed values are: true, false.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- enable_rpc_deprecation=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=enable_rpc_deprecation=true ./path/to/file.protoInput example:
syntax = "proto3";
package helloproto.v1;
import "google/api/annotations.proto";
option go_package = "helloproto/v1;helloproto";
service EchoService {
rpc Hello(HelloReq) returns (HelloResp) {
option deprecated = true;
option (google.api.http) = {get: "/api/hello"};
}
}
message HelloReq {
string name = 1;
}
message HelloResp {
string message = 1;
}Output:
swagger: "2.0"
info:
title: helloproto/v1/example.proto
version: version not set
tags:
- name: EchoService
consumes:
- application/json
produces:
- application/json
paths:
/api/hello:
get:
operationId: EchoService_Hello
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1HelloResp'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/rpcStatus'
parameters:
- name: name
in: query
required: false
type: string
tags:
- EchoService
deprecated: true
definitions:
protobufAny:
type: object
properties:
'@type':
type: string
additionalProperties: {}
rpcStatus:
type: object
properties:
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
type: object
$ref: '#/definitions/protobufAny'
v1HelloResp:
type: object
properties:
message:
type: stringDeprecating parameters
With the enable_field_deprecation option, OpenAPI parameters will be marked as deprecated based on the protobuf deprecated field option. Allowed values are: true, false.
For example, if you are using buf:
version: v1
plugins:
- name: openapiv2
out: .
opt:
- enable_field_deprecation=trueor with protoc
protoc --openapiv2_out=. --openapiv2_opt=enable_field_deprecation=true ./path/to/file.protoInput example:
message SearchRequest {
string legacy_filter = 1 [deprecated = true];
string query = 2;
}Output (excerpt):
paths:
/v1/search:
get:
parameters:
- name: legacy_filter
in: query
required: false
type: string
deprecated: true
- name: query
in: query
required: false
type: stringIf you prefer to leave the protobuf definition active, you can use the field_configuration.deprecated annotation on the openapiv2_field option instead:
message SearchRequest {
string legacy_filter = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Legacy filter syntax. Prefer the new 'query' field."
field_configuration: { deprecated: true }
}
];
string query = 2;
}This keeps the protobuf field untouched but still emits a deprecated parameter in the generated spec:
paths:
/v1/search:
get:
parameters:
- name: legacy_filter
in: query
required: false
type: string
description: Legacy filter syntax. Prefer the new 'query' field.
deprecated: true
- name: query
in: query
required: false
type: stringIf you set both the protobuf deprecated = true option and field_configuration.deprecated, the OpenAPI parameter is marked as deprecated regardless of the enable_field_deprecation option.
{% endraw %}
Docs Mapping Customizing Your Gateway (`docs/docs/mapping/customizing_your_gateway.md`)
layout: default title: Customizing your gateway nav_order: 5 parent: Mapping
Customizing your gateway
Message serialization
Custom serializer
You might want to serialize request/response messages in MessagePack instead of JSON, for example:
Write a custom implementation of
Marshaler.
See Custom marshalers for some additional
customization options.Register your marshaler with
WithMarshalerOption.e.g.
govar m your.MsgPackMarshaler mux := runtime.NewServeMux( runtime.WithMarshalerOption("application/x-msgpack", m), )
You can see the default implementation for JSON for reference.
Using proto names in JSON
The protocol buffer compiler generates camelCase JSON tags that are used by default.
If you want to use the exact case used in the proto files, set UseProtoNames: true:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
}),
)Pretty-print JSON responses when queried with ?pretty
You can have Elasticsearch-style ?pretty support in your gateway's endpoints as follows:
Wrap the ServeMux using a stdlib
http.HandlerFuncthat translates the provided query parameter into a customAcceptheader.Register a pretty-printing marshaler for that MIME code.
For example:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption("application/json+pretty", &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
Indent: " ",
Multiline: true, // Optional, implied by presence of "Indent".
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
}),
)
prettier := func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// checking Values as map[string][]string also catches ?pretty and ?pretty=
// r.URL.Query().Get("pretty") would not.
if _, ok := r.URL.Query()["pretty"]; ok {
r.Header.Set("Accept", "application/json+pretty")
}
h.ServeHTTP(w, r)
})
}
http.ListenAndServe(":8080", prettier(mux))Now, either when passing the header Accept: application/json+pretty or appending ?pretty to your HTTP endpoints, the response will be pretty-printed.
Note that this will conflict with any methods having input messages with fields named pretty; also, this example code does not remove the query parameter pretty from further processing.
Customize unmarshaling per Content-Type
Having different unmarshaling options per Content-Type is as easy as configuring a custom marshaler:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption("application/json+strict", &runtime.JSONPb{
UnmarshalOptions: &protojson.UnmarshalOptions{
DiscardUnknown: false, // explicit "false", &protojson.UnmarshalOptions{} would have the same effect
},
}),
)Note: The incoming Authorization HTTP header can not be removed or overwritten. It will always be forwarded in the gRPC metadata under the authorization key. However, values can be appended.
Mapping from HTTP request headers to gRPC client metadata
You might not like the default mapping rule and might want to pass through all the HTTP headers, for example:
Write a
HeaderMatcherFunc.Register the function with
WithIncomingHeaderMatchere.g.
gofunc CustomMatcher(key string) (string, bool) { switch key { case "X-Custom-Header1": return key, true case "X-Custom-Header2": return "custom-header2", true default: return key, false } } mux := runtime.NewServeMux( runtime.WithIncomingHeaderMatcher(CustomMatcher), )
To keep the default mapping rule alongside with your own rules write:
func CustomMatcher(key string) (string, bool) {
switch key {
case "X-User-Id":
return key, true
default:
return runtime.DefaultHeaderMatcher(key)
}
}It will work with both:
$ curl --header "x-user-id: 100d9f38-2777-4ee2-ac3b-b3a108f81a30" ...and
$ curl --header "X-USER-ID: 100d9f38-2777-4ee2-ac3b-b3a108f81a30" ...To access this header on gRPC server side use:
userID := ""
if md, ok := metadata.FromIncomingContext(ctx); ok {
if uID, ok := md["x-user-id"]; ok {
userID = strings.Join(uID, ",")
}
}Mapping from gRPC server metadata to HTTP response headers
Use WithOutgoingHeaderMatcher. See gRPC metadata docs for more info on sending / receiving gRPC metadata, for example:
if appendCustomHeader {
grpc.SendHeader(ctx, metadata.New(map[string]string{
"x-custom-header1": "value",
}))
}Mutate response messages or set response headers
Set HTTP headers
You might want to return a subset of response fields as HTTP response headers; You might want to simply set an application-specific token in a header. Or you might want to mutate the response messages to be returned.
- Write a filter function.
func myFilter(ctx context.Context, w http.ResponseWriter, resp proto.Message) error {
t, ok := resp.(*externalpb.Tokenizer)
if ok {
w.Header().Set("X-My-Tracking-Token", t.Token)
t.Token = ""
}
return nil
}- Register the filter with
WithForwardResponseOption
e.g.
mux := runtime.NewServeMux(
runtime.WithForwardResponseOption(myFilter),
)Controlling HTTP response status codes
To have the most control over the HTTP response status codes, you can use custom metadata.
While handling the rpc, set the intended status code:
_ = grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "401"))Now, before sending the HTTP response, we need to check for this metadata pair and explicitly set the status code for the response if found.
To do so, create a function and hook it into the gRPC-Gateway as a Forward Response Option.
The function looks like this:
func httpResponseModifier(ctx context.Context, w http.ResponseWriter, p proto.Message) error {
md, ok := runtime.ServerMetadataFromContext(ctx)
if !ok {
return nil
}
// set http status code
if vals := md.HeaderMD.Get("x-http-code"); len(vals) > 0 {
code, err := strconv.Atoi(vals[0])
if err != nil {
return err
}
// delete the headers to not expose any grpc-metadata in http response
delete(md.HeaderMD, "x-http-code")
delete(w.Header(), "Grpc-Metadata-X-Http-Code")
w.WriteHeader(code)
}
return nil
}And it gets hooked into the gRPC-Gateway with:
gwMux := runtime.NewServeMux(
runtime.WithForwardResponseOption(httpResponseModifier),
)Additional responses can be added to the Protocol Buffer definitions to match the new status codes:
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "201"
value: {
description: "A successful response."
schema: {
json_schema: {
ref: ".mypackage.HelloReply"
}
}
}
}
};
}
rpc SayGoodbye (GoodbyeRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/v1/example/echo/{id}"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "204"
value: {
description: "A successful response."
schema: {}
}
}
};
}
}Fully Overriding Custom HTTP Responses
To fully override custom HTTP responses, you can use both a Forward Response Option and a Custom Marshaler.
For example with proto response message as:
message CreateUserResponse {
string name = 1;
}The default HTTP response:
HTTP 200 OK
Content-Type: application/json
{"name":"John Doe"}But you want to return a 201 Created status code along with a custom response structure:
HTTP 201 Created
Content-Type: application/json
{"success":true,"data":{"name":"John Doe"}}First, set up the gRPC-Gateway with the custom options:
mux := runtime.NewServeMux(
runtime.WithForwardResponseOption(setStatus),
runtime.WithForwardResponseRewriter(responseEnvelope),
)Define the setStatus function to handle specific response types:
func setStatus(ctx context.Context, w http.ResponseWriter, m protoreflect.ProtoMessage) error {
switch v := m.(type) {
case *pb.CreateUserResponse:
w.WriteHeader(http.StatusCreated)
}
// keep default behavior
return nil
}Define the responseEnvelope function to rewrite the response to a different type/shape:
func responseEnvelope(_ context.Context, response proto.Message) (interface{}, error) {
switch v := response.(type) {
case *pb.CreateUserResponse:
// wrap the response in a custom structure
return map[string]any{
"success": true,
"data": response,
}, nil
}
return response, nil
}In this setup:
- The
setStatusfunction intercepts the response and uses its type to send201 Createdonly when it sees*pb.CreateUserResponse. - The
responseEnvelopefunction ensures that specific types of responses are wrapped in a custom structure before being sent to the client.
❗ NOTE: Using WithForwardResponseRewriter is partially incompatible with OpenAPI annotations. Because response
rewriting happens at runtime, it is not possible to represent that in protoc-gen-openapiv2 output.
Error handler
To override error handling for a *runtime.ServeMux, use theruntime.WithErrorHandler option. This will configure all unary error
responses to pass through this error handler.
Stream Error Handler
The error handler described in the previous section applies only to RPC methods that have a unary response.
When the method has a streaming response, gRPC-Gateway handles that by emitting a newline-separated stream of "chunks". Each chunk is an envelope that can contain either a response message or an error. Only the last chunk will include an error, and only when the RPC handler ends abnormally (i.e. with an error code).
Because of the way the errors are included in the response body, the other error handler signature is insufficient. So for server streams, you must install a different error handler:
mux := runtime.NewServeMux(
runtime.WithStreamErrorHandler(handleStreamError),
)The signature of the handler is much more rigid because we need to know the structure of the error payload to properly encode the "chunk" schema into an OpenAPI spec.
So the function must return a *runtime.StreamError. The handler can choose to omit some fields and can filter/transform the original error, such as stripping stack traces from error messages.
Here's an example custom handler:
// handleStreamError overrides default behavior for computing an error
// message for a server stream.
//
// It uses a default "502 Bad Gateway" HTTP code, only emits "safe"
// messages and does not set the details field (so it will
// be omitted from the resulting JSON object that is sent to client).
func handleStreamError(ctx context.Context, err error) *status.Status {
code := codes.Internal
msg := "unexpected error"
if s, ok := status.FromError(err); ok {
code = s.Code()
// default message, based on the gRPC status
msg = s.Message()
// see if error details include "safe" message to send
// to external callers
for _, msg := range s.Details() {
if safe, ok := msg.(*SafeMessage); ok {
msg = safe.Text
break
}
}
}
return status.Errorf(code, msg)
}If no custom handler is provided, the default stream error handler will include any gRPC error attributes (code, message, detail messages), if the error being reported includes them. If the error does not have these attributes, a gRPC code of Unknown (2) is reported.
Controlling path parameter unescaping
By default, gRPC-Gateway unescapes the entire URL path string attempting to route a request. This causes routing errors when the path parameter contains an illegal character such as /.
To replicate the behavior described in google.api.http, use runtime.WithUnescapingMode() to configure the unescaping behavior, as in the example below:
mux := runtime.NewServeMux(
runtime.WithUnescapingMode(runtime.UnescapingModeAllExceptReserved),
)For multi-segment parameters (e.g. {id=**}) RFC 6570 Reserved Expansion characters are left escaped and the gRPC API will need to unescape them.
To replicate the default V2 escaping behavior but also allow passing pct-encoded / characters, the ServeMux can be configured as in the example below:
mux := runtime.NewServeMux(
runtime.WithUnescapingMode(runtime.UnescapingModeAllCharacters),
)Routing Error handler
To override the error behavior when *runtime.ServeMux was not able to serve the request due to routing issues, use the runtime.WithRoutingErrorHandler option.
This will configure all HTTP routing errors to pass through this error handler. The default behavior is to map HTTP error codes to gRPC errors.
HTTP statuses and their mappings to gRPC statuses:
- HTTP
404 Not Found-> gRPC5 NOT_FOUND - HTTP
405 Method Not Allowed-> gRPC12 UNIMPLEMENTED - HTTP
400 Bad Request-> gRPC3 INVALID_ARGUMENT
This method is not used outside of the initial routing.
Customizing Routing Errors
If you want to retain HTTP 405 Method Not Allowed instead of allowing it to be converted to the equivalent of the gRPC 12 UNIMPLEMENTED, which is HTTP 501 Not Implemented you can use the following example:
func handleRoutingError(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
if httpStatus != http.StatusMethodNotAllowed {
runtime.DefaultRoutingErrorHandler(ctx, mux, marshaler, w, r, httpStatus)
return
}
// Use HTTPStatusError to customize the DefaultHTTPErrorHandler status code
err := &runtime.HTTPStatusError{
HTTPStatus: httpStatus,
Err: status.Error(codes.Unimplemented, http.StatusText(httpStatus)),
}
runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err)
}To use this routing error handler, construct the mux as follows:
mux := runtime.NewServeMux(
runtime.WithRoutingErrorHandler(handleRoutingError),
)Disabling X-HTTP-Method-Override
By default, the gRPC-Gateway allows clients to send a POST request with anX-HTTP-Method-Override header to override the HTTP method. For example, aPOST request with X-HTTP-Method-Override: GET will be routed as if it were
a GET request. This is part of the path length fallback behavior, which
allows HTML forms (which only support GET and POST) to call other methods.
This can lead to HTTP method confusion when your gateway sits behind a Web
Application Firewall (WAF) or reverse proxy that enforces method-based access
controls. For example, if a WAF is configured to only allow POST requests to
a particular endpoint, a client could send a POST withX-HTTP-Method-Override: DELETE and the gateway would route the request to theDELETE handler, bypassing the WAF's intended restrictions. The WAF sees aPOST request, but the gateway processes it as a DELETE.
To disable the X-HTTP-Method-Override header handling, use theWithDisableHTTPMethodOverride option:
mux := runtime.NewServeMux(
runtime.WithDisableHTTPMethodOverride(),
)This disables only the method override header. The path length fallback (routing
a POST with Content-Type: application/x-www-form-urlencoded to a matchingGET handler) remains available unless separately disabled withWithDisablePathLengthFallback.
Docs Mapping Examples (`docs/docs/mapping/examples.md`)
layout: default title: Examples nav_order: 0 parent: Mapping
Examples
Examples are available under examples/internal directory.
proto/examplepb/echo_service.proto,proto/examplepb/a_bit_of_everything.proto,proto/examplepb/unannotated_echo_service.proto:
protobuf service definitions.proto/examplepb/echo_service.pb.go,proto/examplepb/a_bit_of_everything.pb.go,proto/examplepb/unannotated_echo_service.pb.go:
generated Go service stubs and types.proto/examplepb/echo_service.pb.gw.go,proto/examplepb/a_bit_of_everything.pb.gw.go,proto/examplepb/unannotated_echo_service.pb.gw.go:
generated gRPC-Gateway clients.proto/examplepb/unannotated_echo_service.yaml:
gRPC API Configuration forunannotated_echo_service.proto.server/main.go:
service implementation.main.go:
entrypoint of the generated reverse proxy.
To use the same port for custom HTTP handlers (e.g. serving swagger.json),
gRPC-Gateway, and a gRPC server, see this code example by CoreOS (and its accompanying
blog post).
Docs Mapping Grpc Api Configuration (`docs/docs/mapping/grpc_api_configuration.md`)
layout: default title: gRPC API Configuration nav_order: 3 parent: Mapping
gRPC API Configuration
In some situations annotating the proto file of service is not an option. For example, you might not have control over the proto file, or you might want to expose the same gRPC API multiple times in completely different ways.
gRPC-Gateway supports 2 ways of dealing with these situations:
`generate_unbound_methods`
Providing this parameter to the protoc plugin will make it produce the HTTP mapping even for methods without any HttpRule annotation. This is similar to how Cloud Endpoints behaves and uses the way gRPC itself maps to HTTP/2:
- HTTP method is
POST - URI path is built from the service's name and method:
/<fully qualified service name>/<method name>(e.g.:/my.package.EchoService/Echo) - HTTP body is the serialized protobuf message.
NOTE: the same option is also supported by the gen-openapiv2 plugin.
`strict_deep_wildcard`
By default a deep wildcard (**) that follows another path segment may match zero path segments, so /v3/files/{path=**} also matches /v3/files with an empty path. Providing strict_deep_wildcard=true to the protoc-gen-grpc-gateway plugin makes such patterns require at least one remaining path segment, so /v3/files no longer matches and can be routed to a separately registered /v3/files handler. A request with a trailing slash (/v3/files/) still matches with an empty capture, and a bare ** with nothing before it still matches zero segments.
protoc --grpc-gateway_out=. --grpc-gateway_opt=strict_deep_wildcard=true ...The generated code requires a version of the runtime package that includes runtime.NewPatternWithOptions.
Using an external configuration file
Google Cloud Platform offers a way to do this for services
hosted with them called "gRPC API Configuration". It can be used to define the behavior of a gRPC API service without modifications to the service itself in the form of YAML configuration files.
gRPC-Gateway generators implement the HTTP rules part of this specification. This allows you to take a completely unannotated service proto file, add a YAML file describing its HTTP endpoints and use them together like an annotated proto file with the gRPC-Gateway generators.
OpenAPI options may also be configured via "OpenAPI Configuration" in the form of YAML configuration files.
Usage of gRPC API Configuration YAML files
The following is equivalent to the basic README.md example but without direct
annotation for gRPC-Gateway in the proto file. Only some steps require minor changes to use a gRPC API Configuration YAML file instead:
Define your service in gRPC as usual
your_service.proto:
protobufsyntax = "proto3"; package your.service.v1; option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; message StringMessage { string value = 1; } service YourService { rpc Echo(StringMessage) returns (StringMessage) {} }Instead of annotating the proto file in this step leave it untouched
and create ayour_service.yamlwith the following content:yamltype: google.api.Service config_version: 3 http: rules: - selector: your.service.v1.YourService.Echo post: /v1/example/echo body: "*"Use a linter to validate your YAML.
Generate gRPC stub as before
shprotoc -I . \ --go_out ./gen/go/ \ --go_opt paths=source_relative \ --go-grpc_out ./gen/go/ \ --go-grpc_opt paths=source_relative \ your/service/v1/your_service.proto
It will generate a stub file with path ./gen/go/your/service/v1/your_service.pb.go.
Implement your service in gRPC as usual
Generate the reverse-proxy. Here we have to pass the path to
theyour_service.yamlin addition to the proto file:shprotoc -I . \ --grpc-gateway_out ./gen/go \ --grpc-gateway_opt paths=source_relative \ --grpc-gateway_opt grpc_api_configuration=path/to/your_service.yaml \ your/service/v1/your_service.protoThis will generate a reverse proxy
gen/go/your/service/v1/your_service.pb.gw.gothat is identical to the one produced for the annotated proto.Using Buf with one configuration for multiple services
When a single gRPC API Configuration YAML file contains selectors for
services defined in different directories, setstrategy: allfor each
local Buf plugin that uses the configuration. Buf invokes local plugins
once per directory by default, so each invocation sees only part of the
schema and reports rules for services in other directories as unmatched.yamlversion: v2 plugins: - local: protoc-gen-grpc-gateway out: gen/go strategy: all opt: - paths=source_relative - grpc_api_configuration=path/to/your_service.yaml - local: protoc-gen-openapiv2 out: gen/openapiv2 strategy: all opt: - grpc_api_configuration=path/to/your_service.yamlSee Buf's invocation strategy documentation.
In situations where you only need the reverse-proxy you can use the
standalone=trueoption when generating the code. This will ensure thetypesused withinyour_service.pb.gw.goreference the external source appropriately.textprotoc -I . \ --grpc-gateway_out ./gen/go \ --grpc-gateway_opt paths=source_relative \ --grpc-gateway_opt standalone=true \ --grpc-gateway_opt grpc_api_configuration=path/to/your_service.yaml \ your/service/v1/your_service.protoGenerate the optional your_service.swagger.json
shprotoc -I . --openapiv2_out ./gen/go \ --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ your/service/v1/your_service.protoor using an OpenAPI configuration file
shprotoc -I . --openapiv2_out ./gen/go \ --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ --openapiv2_opt openapi_configuration=path/to/your_service_swagger.yaml \ your/service/v1/your_service.protoFor an example of an OpenAPI configuration file, see unannotated_echo_service.swagger.yaml, which adds OpenAPI options to unannotated_echo_service.proto.
shprotoc -I . --openapiv2_out ./gen/go \ --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ your/service/v1/your_service.proto
All other steps work as before. If you want you can remove the googleapis include path in step 3 and 4 as the unannotated proto no longer requires them.
Docs Mapping Httpbody Messages (`docs/docs/mapping/httpbody_messages.md`)
layout: default title: HttpBody Messages nav_order: 1 parent: Mapping
HttpBody Messages
The HTTPBody messages allow a response message to be specified with custom data content and a custom content-type header. The values included in the HTTPBody response will be used verbatim in the returned message from the gateway. Make sure you format your response carefully!
google.api.HttpBody is also the native gRPC-Gateway response shape to use for
binary downloads and other raw payload responses where you need to control the
returned content type directly.
Example Usage
- Define your service in gRPC with an
HttpBodyresponse message.
import "google/api/httpbody.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
service HttpBodyExampleService {
rpc HelloWorld(google.protobuf.Empty) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/helloworld"
};
}
rpc Download(google.protobuf.Empty) returns (stream google.api.HttpBody) {
option (google.api.http) = {
get: "/download"
};
}
}- Generate gRPC and reverse-proxy stubs and implement your service.
Example service implementation
func (*HttpBodyExampleService) Helloworld(ctx context.Context, in *empty.Empty) (*httpbody.HttpBody, error) {
return &httpbody.HttpBody{
ContentType: "text/html",
Data: []byte("Hello World"),
}, nil
}
func (HttpBodyExampleService) Download(_ *empty.Empty, stream HttpBodyExampleService_DownloadServer) error {
msgs := []*httpbody.HttpBody{
{
ContentType: "text/html",
Data: []byte("Hello 1"),
},
{
ContentType: "text/html",
Data: []byte("Hello 2"),
},
}
for _, msg := range msgs {
if err := stream.Send(msg); err != nil {
return err
}
}
return nil
}Docs Mapping Openapi V3 (`docs/docs/mapping/openapi_v3.md`)
layout: default title: OpenAPI 3.1 Output nav_order: 7 parent: Mapping
OpenAPI 3.1 Output
{: .warning }
Alpha — output is not yet stable. protoc-gen-openapiv3 is a new
generator and the exact JSON shape it emits may change between minor
releases as the mapping rules settle in response to user feedback.
Specifically expect the encodings for oneofs, wrapper types, enums, and
path-template expansion to evolve, and expect tooling-compatibility
compromises (documented inline in the generator source) to be revisited
as consumer OpenAPI 3.1 support matures. If you need a production-stable
OpenAPI pipeline today, use protoc-gen-openapiv2.
{: .note }protoc-gen-openapiv3 emits OpenAPI 3.1.0 JSON directly fromgoogle.api.http annotations. It is intentionally smaller in scope thanprotoc-gen-openapiv2 and does not
consume the grpc.gateway.protoc_gen_openapiv2.options annotations. If
you rely on those options, continue to use the v2 generator.
What it does
protoc-gen-openapiv3 walks every proto file it is asked to generate, finds
services with HTTP bindings, and emits one OpenAPI 3.1.0 JSON document per
proto file (foo.proto → foo.openapi.json). Files with no HTTP-bound
services produce no output.
The output is deterministic and byte-stable across runs: paths are emitted in
RPC declaration order, component schemas are sorted alphabetically, and
response codes are emitted default-first then sorted.
What it does not do
The generator is deliberately opinionated. The following are not supported today:
- The
grpc.gateway.protoc_gen_openapiv2.optionsannotation set. - OpenAPI 2.0 / Swagger output (use
protoc-gen-openapiv2for that). - YAML output.
- Alternative naming strategies. Component names are always the
fully-qualified proto name with the leading dot stripped
(e.g.lib.v1.Book). - Integer enums. Enums are always rendered as strings.
Features can be added back as concrete needs emerge — if you want one of the
above, please open an issue describing your use case.
Plugin options
`disable_default_errors`
By default, every operation gets an auto-injected default response entry
pointing to the google.rpc.Status component schema. If you use a custom
error handler that returns a different response shape, you can suppress this
behaviour with the disable_default_errors option.
With buf (buf.gen.yaml):
version: v2
plugins:
- local: protoc-gen-openapiv3
out: .
opt:
- disable_default_errors=trueWith protoc:
protoc -I. \
--openapiv3_out=. \
--openapiv3_opt=disable_default_errors=true \
path/to/your/service.protoWhen set, neither the default response entry nor the google.rpc.Status
component schema are emitted.
`visibility_restriction_selectors`
See Hiding fields, methods, services and enum values below.
Installation
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3@latestUsage with buf
Add the plugin to your buf.gen.yaml:
version: v2
plugins:
- local: protoc-gen-openapiv3
out: .Then run buf generate as usual. A .openapi.json file will appear next to
each proto file that declares HTTP bindings.
Usage with protoc
protoc -I. \
--openapiv3_out=. \
path/to/your/service.protoHiding fields, methods, services and enum values
If you require internal or unreleased fields and APIs to be hidden from
your API documentation,google.api.VisibilityRule
annotations can be added to services, methods, fields and enum values.
The generator drops any annotated element whose restriction labels do
not overlap with the configured visibility_restriction_selectors.
Elements without a VisibilityRule annotation are always emitted.
visibility_restriction_selectors can be declared multiple times to
include multiple visibility restrictions in the output. For example,
with buf:
version: v2
plugins:
- local: protoc-gen-openapiv3
out: .
opt:
- visibility_restriction_selectors=PREVIEW
- visibility_restriction_selectors=INTERNALor with protoc:
protoc --openapiv3_out=. \
--openapiv3_opt=visibility_restriction_selectors=PREVIEW \
--openapiv3_opt=visibility_restriction_selectors=INTERNAL \
./path/to/file.protoThe restriction labels are arbitrary. INTERNAL and PREVIEW are the
conventional pair, but any label scheme works (ALPHA, BETA,RELEASED, ...) as long as the annotations and the selector flag use
the same vocabulary.
When a service is hidden, all of its methods are hidden along with it
and the service's tag is omitted from tags[]. When every field in aoneof group is hidden, the group's "at most one set" constraint is
also dropped.
Visibility annotations on fields used as HTTP path parameters (viagoogle.api.http path templates) are ignored — the path template
itself always exposes the parameter's name. To hide an operation that
takes a restricted input, apply google.api.method_visibility to the
method (or google.api.api_visibility to the whole service) instead
of annotating the path field.
If every value of an enum ends up hidden, the enum's component schema
is still emitted (a visible field may reference it) but falls back to
an unconstrained type: string. A log line is written when this
happens; broaden the selectors or hide the referring fields to silence
it.
Mapping rules
The mapping from proto constructs to OpenAPI is fixed and summarised below.
Services and methods
| Proto | OpenAPI |
|---|---|
| Service | Tag (tags[]) plus tags on each operation |
| Method | Operation under a PathItem |
| Method leading comment | Operation summary / description |
Method deprecated=true |
Operation deprecated: true |
| Additional bindings | Extra operations; operationId suffixed _ |
The default operationId is <Service>_<Method>. When a method has multiple
HTTP bindings, the first uses the bare ID and subsequent bindings append_1, _2, ... so the spec remains valid.
The service's tag is named after the service and described by its leading
comment. The service-level openapiv3_tag annotation overrides it: a
non-empty name renames the tag, which the service's operations then use by
default, and description, external_docs and extensions set the
corresponding fields of the OpenAPI
Tag Object. An
operation-level openapiv3_operation.tags list still replaces the default for
that operation.
import "protoc-gen-openapiv3/options/annotations.proto";
service BookService {
option (grpc.gateway.protoc_gen_openapiv3.options.openapiv3_tag) = {
name: "Books"
description: "Manage the books in the library."
external_docs: {
description: "Library guide"
url: "https://example.com/docs/books"
}
};
rpc GetBook(GetBookRequest) returns (Book) {
option (google.api.http) = {get: "/v1/books/{id}"};
}
}Parameters
Path parameters from the
google.api.httpURL template becomein: path
parameters.Constrained path templates like
{name=shelves/*}are expanded into
the OpenAPI URL verbatim: the literal prefix stays in the URL and each
wildcard becomes its own OpenAPI parameter. This is required because the
proto field binds to the full matched substring (e.g.shelves/abc), so
a client following the OpenAPI URL must actually send/shelves/abc
rather than just/abc. Examples:Proto template OpenAPI URL /v1/{name}/v1/{name}/v1/{name=shelves/*}/v1/shelves/{name}/v1/{name=shelves/*/books/*}/v1/shelves/{name}/books/{name_1}/v1/{name=files/**}/v1/files/{name}When a single proto field expands into multiple wildcards, the second and
subsequent parameters are suffixed_1,_2, ... so the OpenAPI URL
stays valid. All synthetic parameters point back at the same underlying
proto field, which the grpc-gateway runtime reconstructs from the full
matched substring at request time.Fields that are neither path parameters nor part of the request body
becomein: queryparameters. This also applies to nested fields: with/v1/{id.value},id.valueis only a path parameter, while the other
fields ofidare still flattened into query parameters.body="*"synthesises an inline object schema that includes every request
field except path parameters.body="field"uses that single field's type as the request body.
Schemas
- Messages become component schemas under
#/components/schemas/. - Enums become string-valued component schemas.
repeatedfields producetype: array.- Map fields produce
type: objectwithadditionalProperties. - 64-bit integer fields are emitted as JSON strings, matching protojson.
- Well-known types are inlined, not referenced (see below).
- Field descriptions are populated from leading proto comments.
google.api.field_behaviormaps torequired,readOnly, andwriteOnlyon the parent schema.deprecated = trueon a field setsdeprecated: trueon the property.
Oneofs
Every field inside a oneof appears as a normal property on the component
schema, and each oneof group contributes an "at most one set" constraint
on top of those properties. The constraint is expressed as a JSON SchemaoneOf whose options are:
- a "none of the fields are set" guard of the form
{"not": {"anyOf": [{"required": ["fieldA"]}, {"required": ["fieldB"]}, ...]}}; - one
{"required": ["fieldN"]}option per field in the group.
Because oneOf matches exactly one sub-schema:
- zero fields set → only option 1 matches → passes,
- exactly one field set → only that field's
requiredoption matches → passes, - two or more set → multiple
requiredoptions match → fails validation.
This matches proto3 oneof semantics, which allow either zero or one
member to be set.
Messages with a single oneof group hoist the constraint directly onto
the component schema's oneOf. Messages with multiple groups wrap their
group constraints in allOf so each group is enforced independently.
Single-field oneof groups produce a trivially-true constraint and are
skipped for output hygiene. Synthetic proto3-optional oneofs are treated
as regular optional fields (the proto compiler models optional string foo = 1; as a one-element synthetic oneof).
Well-known types
Well-known types are inlined as their protojson representation:
| WKT | Schema |
|---|---|
google.protobuf.Timestamp |
string, format: date-time |
google.protobuf.Duration |
string |
google.protobuf.FieldMask |
string |
google.protobuf.StringValue |
string |
google.protobuf.BytesValue |
string, format: byte |
google.protobuf.Int32Value |
integer, format: int32 |
google.protobuf.UInt32Value |
integer, format: int64 |
google.protobuf.Int64Value |
string, format: int64 |
google.protobuf.UInt64Value |
string, format: uint64 |
google.protobuf.FloatValue |
number, format: float |
google.protobuf.DoubleValue |
number, format: double |
google.protobuf.BoolValue |
boolean |
google.protobuf.Empty |
object |
google.protobuf.Struct |
object |
google.protobuf.Value |
unconstrained |
google.protobuf.ListValue |
array of unconstrained |
google.protobuf.NullValue |
null |
google.protobuf.Any |
object with @type plus open |
Wrapper types are intentionally not marked nullable. The strictly-correct
JSON Schema 2020-12 form for google.protobuf.StringValue would betype: ["string", "null"], but no Go OpenAPI generator in the ecosystem
handles that form today: openapi-generator-cli's Go target produces
unbuildable code, and oapi-codegen (including its experimental 3.1 fork)
errors with unhandled Schema type: &[string null] on request bodies.
Until tooling catches up, the generator describes wrappers as their
underlying primitive. The grpc-gateway runtime still accepts a JSON null
on the wire regardless, because protojson treats wrappers as optional.
RPCs returning google.protobuf.Empty emit a 200 with an object schema
body ({}), matching what the grpc-gateway runtime actually writes on
the wire. The HTTP-conventional 204 No Content would be more idiomatic
but conflicts with runtime behaviour, so generated clients would reject
valid responses.
Error responses
Every operation automatically gets a default response keyed to a
component schema named google.rpc.Status with the standard code,message, and details[] fields. This matches what grpc-gateway returns
on error paths. Use the disable_default_errors plugin option to suppress
this behaviour when using a custom error handler.
Security schemes
Security schemes are declared once in the file-level openapiv3_document
annotation, under components.security_schemes, and referenced by name fromsecurity requirements. A document-level security list applies to every
operation in the file; an operation-level security list replaces it for that
operation. Each entry in a security list is an alternative, and all schemes
named within one entry must be satisfied together.
The fields map to the OpenAPI
Security Scheme Object
and Security Requirement Object.
Generation fails if a scheme is missing a field the spec requires for its type,
or if a requirement names a scheme that isn't declared in the same file.
import "protoc-gen-openapiv3/options/annotations.proto";
option (grpc.gateway.protoc_gen_openapiv3.options.openapiv3_document) = {
components: {
security_schemes: {
key: "BearerAuth"
value: {
type: TYPE_HTTP
scheme: "bearer"
bearer_format: "JWT"
}
}
security_schemes: {
key: "ApiKeyAuth"
value: {
type: TYPE_API_KEY
name: "X-API-Key"
in: IN_HEADER
}
}
security_schemes: {
key: "OAuth2"
value: {
type: TYPE_OAUTH2
flows: {
authorization_code: {
authorization_url: "https://example.com/oauth/authorize"
token_url: "https://example.com/oauth/token"
scopes: {
key: "read"
value: "Read access"
}
scopes: {
key: "write"
value: "Write access"
}
}
}
}
}
}
// Every operation requires a bearer token unless it says otherwise.
security: {
schemes: {
key: "BearerAuth"
value: {}
}
}
};An operation can require different schemes, ask for OAuth2 scopes, or accept
any of several alternatives:
rpc UpdateBook(UpdateBookRequest) returns (Book) {
option (google.api.http) = {
patch: "/v1/books/{book.id}"
body: "book"
};
// Either an OAuth2 token with the "write" scope, or an API key.
option (grpc.gateway.protoc_gen_openapiv3.options.openapiv3_operation) = {
security: {
schemes: {
key: "OAuth2"
value: {scopes: "write"}
}
}
security: {
schemes: {
key: "ApiKeyAuth"
value: {}
}
}
};
}To make authentication optional for an operation, add an empty requirement.
It renders as {} in the operation's security list:
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
option (google.api.http) = {get: "/v1/books"};
option (grpc.gateway.protoc_gen_openapiv3.options.openapiv3_operation) = {
security: {}
};
}Mutual TLS (TYPE_MUTUAL_TLS) needs no other fields, and OpenID Connect
(TYPE_OPEN_ID_CONNECT) needs open_id_connect_url.
Example
Given:
syntax = "proto3";
package example.v1;
import "google/api/annotations.proto";
service EchoService {
rpc Echo(EchoRequest) returns (EchoResponse) {
option (google.api.http) = {
post: "/v1/echo"
body: "*"
};
}
}
message EchoRequest { string message = 1; }
message EchoResponse { string message = 1; }The generator emits echo.openapi.json with one POST /v1/echo operation,
an EchoResponse component schema, and the auto-injectedgoogle.rpc.Status default error response. Seeprotoc-gen-openapiv3/internal/genopenapi/testdata/simple_echo.openapi.json
for the exact output.
Consuming the output
The generated spec validates as OpenAPI 3.1.0 and can be fed to any 3.1-aware
tool, including openapi-generator-cli
for client generation. An end-to-end example that runs a real grpc-gateway
behind a generated Go client lives atexamples/internal/integration/openapiv3.
Docs Mapping Openapi V3 Merge (`docs/docs/mapping/openapi_v3_merge.md`)
layout: default title: Merging OpenAPI 3.1 Output nav_order: 8 parent: Mapping
Merging OpenAPI 3.1 Output
{: .note }
This page describes openapiv3-merge, the post-processing tool that
combines protoc-gen-openapiv3 output into a single OpenAPI document. The
generator itself is documented under
OpenAPI 3.1 Output.
protoc-gen-openapiv3 emits one OpenAPI document per
proto file, matching the protobuf convention of 1:1 input-to-output
files. Many OpenAPI consumers — UI viewers, client generators, gateway
configurations — expect a single document instead. openapiv3-merge is the
post-processing step that combines the per-file outputs into one.
Installation
go install github.com/grpc-ecosystem/grpc-gateway/v2/openapiv3-merge@latestUsage
openapiv3-merge FILE [FILE ...] > merged.openapi.jsonThe merged document is written to stdout. Errors go to stderr and the
process exits with a non-zero status. Input order matters: the first
input's info, servers, and externalDocs are kept in the merged
document, so put the file with the broadest scope (the one carrying any
shared openapiv3_document annotation) first.
With buf
Generate per-proto-file documents the usual way, then merge them:
#!/usr/bin/env bash
set -euo pipefail
buf generate
# Put the file with the shared openapiv3_document annotation first so
# its info/servers/externalDocs are kept, then everything else in
# sorted order for determinism.
root=gen/api/v1/api.openapi.json
mapfile -t rest < <(find gen -name '*.openapi.json' ! -path "$root" | sort)
openapiv3-merge "$root" "${rest[@]}" > gen/api.openapi.jsonIf you don't have a designated root file, just sort all inputs and accept
whichever sorts first — but set the openapiv3_document annotation on
that proto so the merged output has a sensible title, version, andservers.
With protoc
#!/usr/bin/env bash
set -euo pipefail
protoc -I. \
--openapiv3_out=./gen \
$(find . -name '*.proto')
root=gen/api/v1/api.openapi.json
mapfile -t rest < <(find gen -name '*.openapi.json' ! -path "$root" | sort)
openapiv3-merge "$root" "${rest[@]}" > gen/api.openapi.jsonOne-shot
For trivial trees where any input may go first:
openapiv3-merge $(find . -name '*.openapi.json' | sort) > api.openapi.jsonWhat gets merged, and how
| Field | Rule |
|---|---|
openapi |
Must match across all inputs. |
info, servers, externalDocs |
Taken from the first input. Later inputs' values are dropped. |
paths, webhooks |
Unioned in input order. Path collisions with non-identical content are errors. |
components/* |
Unioned with sorted keys. Same name with non-identical content is an error. |
tags |
Deduplicated by name. Same name with non-identical metadata is an error. |
security |
First input to declare a non-empty array wins. Later inputs that declare a different non-empty array are an error. |
Unknown top-level keys (extensions, x-*) |
Taken from the first input. |
"Non-identical content" is determined by canonical comparison: two values
that differ only in key order compare equal. Since protoc-gen-openapiv3
names component schemas with their fully qualified proto name (e.g.example.v1.User), the same message reused across packages contributes a
single, identical component to every output, and the merger combines them
cleanly.
The first input controls the merged document's info block. If you want a
shared title, version, contact, license, or set of servers across all
merged output, set them with anopenapiv3_document annotation on the
proto file that appears first in your merge invocation.
Output
- Top-level fields are emitted in OpenAPI 3.1.0 declaration order
(openapi,info,servers,paths,webhooks,components,security,tags,externalDocs, then any extensions). pathsandwebhooksare emitted in input order.components/*sub-maps are sorted lexicographically, matching whatprotoc-gen-openapiv3itself emits per file.tagsare emitted in first-occurrence order across inputs.
Example merge errors
When a merge fails, the error message identifies the conflicting field:
openapiv3-merge: paths."/v1/echo": echo.openapi.json redefines an entry with a different valueCommon causes:
- Conflicting path definitions. Two protos declared the same HTTP path
with different bindings. Decide which one is authoritative. - Conflicting component definitions. Two messages with the same fully
qualified proto name but different shapes — usually means a duplicatepackage+ message declaration somewhere in your proto tree. - Conflicting tag metadata. Two
openapiv3_document.tagsannotations
described the same tag with different descriptions. Make them match or
move the metadata to one place. - Mismatched OpenAPI versions. All inputs must declare the same
openapivalue.
See also
- OpenAPI 3.1 Output — the generator that produces the
per-file inputs toopenapiv3-merge. - openapiv3-merge README — quick reference and source.
Docs Mapping Patch Feature (`docs/docs/mapping/patch_feature.md`)
layout: default title: Patch feature nav_order: 2 parent: Mapping
Patch feature
The HTTP PATCH method allows a resource to be partially updated.
If a binding is mapped to patch and the request message has exactly one FieldMask message in it, additional code is rendered for the gateway handler that will populate the FieldMask based on the request body. FieldMask is treated as a regular field by the gateway if the request method is not PATCH, or if the HttpRule body is "*"
There are two scenarios:
- The FieldMask is hidden from the REST request as per the
Google API design guide (as in the first additional binding in the
UpdateV2 example). In this case, the FieldMask is updated from the request body and set in the gRPC request message.- By default this feature is enabled, if you need to disable it, you can use the plugin option
allow_patch_feature=false. - Note: The same option is supported by the
protoc-gen-openapiv2plugin.
- By default this feature is enabled, if you need to disable it, you can use the plugin option
- The FieldMask is exposed to the REST request (as in the second additional binding in the UpdateV2 example). For this case, the field mask is left untouched by the gateway.
Example Usage
Create a PATCH request.
The PATCH request needs to include the message and the update mask.
protobuf// UpdateV2Request request for update includes the message and the update mask message UpdateV2Request { ABitOfEverything abe = 1; google.protobuf.FieldMask update_mask = 2; }Define your service in gRPC
If you want to use PATCH with fieldmask hidden from REST request only include the request message in the body.
protobufrpc UpdateV2(UpdateV2Request) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/v2/example/a_bit_of_everything/{abe.uuid}" body: "abe" additional_bindings { patch: "/v2/example/a_bit_of_everything/{abe.uuid}" body: "abe" } }; }If you want to use PATCH with fieldmask exposed to the REST request then include the entire request message.
protobufrpc UpdateV2(UpdateV2Request) returns (google.protobuf.Empty) { option (google.api.http) = { patch: "/v2a/example/a_bit_of_everything/{abe.uuid}" body: "*" }; }Generate gRPC and reverse-proxy stubs and implement your service.
cURL examples
In the example below, we will partially update our ABitOfEverything resource by passing only the field we want to change. Since we are using the endpoint with field mask hidden we only need to pass the field we want to change ("string_value") and it will keep everything else in our resource the same.
$ curl \
--data '{"stringValue": "strprefix/foo"}' \
-X PATCH \
http://address:port/v2/example/a_bit_of_everything/1If we know what fields we want to update then we can use PATCH with field mask approach. For this, we need to pass the resource and the update_mask. Below only the "single_nested" will get updated because that is what we specify in the field_mask.
$ curl \
--data '{"abe":{"singleNested":{"amount":457},"stringValue":"some value that will not get updated because not in the field mask"},"updateMask":"singleNested"}}' \
-X PATCH \
http://address:port/v2a/example/a_bit_of_everything/1Docs Mapping Using Ref With Responses (`docs/docs/mapping/using_ref_with_responses.md`)
Using arbitrary messages in response description
Assuming a protobuf file of the structure
syntax = "proto3";
package example.service.v1;
import "protoc-gen-openapiv2/options/annotations.proto";
service GenericService {
rpc GenericRPC(GenericRPCRequest) returns (GenericRPCResponse);
}
message GenericRPCRequest {
string id = 1;
}
message GenericRPCResponse {
string result = 1;
}If you want your OpenAPI document to include a custom response for all RPCs defined in this protobuf file, you can add the following:
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
responses: {
key: "400"
value: {
description: "Returned when the request is malformed."
schema: {
json_schema: {ref: ".example.service.v1.GenericResponse"} // Must match the fully qualified name of the message
}
}
}
};
message GenericResponse {
repeated string resources = 1;
repeated string errors = 2;
}When generating, you will see the following response included in your OpenAPI document:
"400": {
"description": "Returned when the request is malformed.",
"schema": {
"$ref": "#/definitions/v1GenericResponse"
}
},The annotation can also be specified per-rpc.
Docs Operations Annotated Context (`docs/docs/operations/annotated_context.md`)
layout: default title: Extracting the HTTP path pattern for a request nav_order: 4 parent: Operations
Extracting the HTTP path pattern for a request
It is often interesting to know what HTTP path pattern was matched for a specific request, for example for metrics. This article explains how to extract the HTTP path pattern from the request context.
Get HTTP Path pattern
- Define the HTTP path in the proto annotation. For example:
syntax = "proto3";
option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/proto/examplepb";
package grpc.gateway.examples.internal.proto.examplepb;
import "google/api/annotations.proto";
service LoginService {
rpc Login (LoginRequest) returns (LoginReply) {
option (google.api.http) = {
post: "/v1/example/login"
body: "*"
};
}
}
message LoginRequest {}
message LoginReply {}- At runtime, get the HTTP path pattern from the annotated context, for example using the
WithMetadatafunction.
You can pass data to your backend by adding them to the gRPC metadata or push them to a metrics server.
mux := runtime.NewServeMux(
runtime.WithMetadata(func(ctx context.Context, r *http.Request) metadata.MD {
md := make(map[string]string)
if method, ok := runtime.RPCMethod(ctx); ok {
md["method"] = method // /grpc.gateway.examples.internal.proto.examplepb.LoginService/Login
}
if pattern, ok := runtime.HTTPPathPattern(ctx); ok {
md["pattern"] = pattern // /v1/example/login
}
return metadata.New(md)
}),
)Docs Operations Aws Gateway Integration (`docs/docs/operations/aws_gateway_integration.md`)
layout: default title: AWS gateway integration nav_order: 3 parent: Operations
AWS gateway integration
Import OpenAPI documentation into AWS API Gateway
The AWS API Gateway service allows importing of an OpenAPI specification to create a REST API. The process is very straightforward and can be found here.
Here are some tips to consider when importing the documentation:
- Remove any circular dependencies (these aren't supported by the parser).
- Remove security-related annotations (These annotations aren't well supported by the parser).
- Max length of fields are reviewed by the parser but the errors aren't self-explanatory. Review the specification to verify that the requirements are met.
- API gateway errors aren't great, but you can use this page for structure validation.
Docs Operations Health Check (`docs/docs/operations/health_check.md`)
layout: default title: Health check nav_order: 1 parent: Operations
Health check
With the [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md)
To use the gRPC health checking protocol you must add the two health checking methods, Watch and Check.
Registering the health server
- Add
google.golang.org/grpc/health/grpc_health_v1to your imports - Register the health server with
grpc_health_v1.RegisterHealthServer(grpcServer, yourService)
Adding the health check methods
- Check method
func (s *serviceServer) Check(ctx context.Context, in *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
return &health.HealthCheckResponse{Status: health.HealthCheckResponse_SERVING}, nil
}- Watch method
func (s *serviceServer) Watch(in *health.HealthCheckRequest, _ health.Health_WatchServer) error {
// Example of how to register both methods but only implement the Check method.
return status.Error(codes.Unimplemented, "unimplemented")
}- You can test the functionality with GRPC health probe.
Adding `/healthz` endpoint to runtime.ServeMux
To automatically register a /healthz endpoint in your ServeMux you can use
the ServeMuxOption WithHealthzEndpoint
which takes in a connection to your registered gRPC server.
This endpoint will forward a request to the Check method described above to really check the health of the
whole system, not only the gateway itself. If your server doesn't implement the health checking protocol each request
to /healthz will result in the following:
{"code":12,"message":"unknown service grpc.health.v1.Health","details":[]}If you've implemented multiple services in your server you can target specific services with the ?service=<service>
query parameter. This will then be added to the health.HealthCheckRequest in the Service property. With that you can
write your own logic to handle that in the health checking methods.
Analogously, to register an {/endpoint/path} endpoint in your ServeMux with a user-defined endpoint path, you can use
the ServeMuxOption WithHealthEndpointAt, which accepts a connection to your registered gRPC server
together with a custom endpointPath string parameter.
Docs Operations Inject Router (`docs/docs/operations/inject_router.md`)
layout: default title: Adding custom routes to the mux nav_order: 0 parent: Operations
Adding custom routes to the mux
The gRPC-Gateway allows you to add custom routes to the serve mux, for example, if you want to support a use case that isn't supported by the gRPC-Gateway, like file uploads.
Example
package main
import (
"context"
"net/http"
pb "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/helloworld"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
)
func main() {
ctx := context.TODO()
mux := runtime.NewServeMux()
// Register generated routes to mux
err := pb.RegisterGreeterHandlerServer(ctx, mux, &GreeterServer{})
if err != nil {
panic(err)
}
// Register custom route for GET /hello/{name}
err = mux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Write([]byte("hello " + pathParams["name"]))
})
if err != nil {
panic(err)
}
http.ListenAndServe(":8080", mux)
}
// GreeterServer is the server API for Greeter service.
type GreeterServer struct {
}
// SayHello implement to say hello
func (h *GreeterServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{
Message: "hello " + req.Name,
}, nil
}Docs Operations Logging (`docs/docs/operations/logging.md`)
layout: default title: Logging the request body pattern for a request nav_order: 5 parent: Operations
Logging the request body pattern for a request
If you want to log the request body of incoming requests, you will need to buffer the body before it reaches the gateway. To log the request body, you can use a middleware http.Handler to buffer the request body before it's consumed.
- Create a
http.Handlermiddleware. ThelogRequestBodyexample middleware logs the request body when the response status code is not 200.
type logResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (rsp *logResponseWriter) WriteHeader(code int) {
rsp.statusCode = code
rsp.ResponseWriter.WriteHeader(code)
}
// Unwrap returns the original http.ResponseWriter. This is necessary
// to expose Flush() and Push() on the underlying response writer.
func (rsp *logResponseWriter) Unwrap() http.ResponseWriter {
return rsp.ResponseWriter
}
func newLogResponseWriter(w http.ResponseWriter) *logResponseWriter {
return &logResponseWriter{w, http.StatusOK}
}
// logRequestBody logs the request body when the response status code is not 200.
func logRequestBody(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lw := newLogResponseWriter(w)
// Note that buffering the entire request body could consume a lot of memory.
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, fmt.Sprintf("failed to read body: %v", err), http.StatusBadRequest)
return
}
clonedR := r.Clone(r.Context())
clonedR.Body = io.NopCloser(bytes.NewReader(body))
h.ServeHTTP(lw, clonedR)
if lw.statusCode != http.StatusOK {
grpclog.Errorf("http error %+v request body %+v", lw.statusCode, string(body))
}
})
}- Wrap the gateway serve mux with the
logRequestBodymiddleware:
mux := runtime.NewServeMux()
// Register generated gateway handlers
s := &http.Server{
Handler: logRequestBody(mux),
}Docs Operations Security Considerations (`docs/docs/operations/security_considerations.md`)
layout: default title: Security considerations nav_order: 6 parent: Operations
Security considerations
Things worth knowing about the defaults before you put a gateway in front of a
service that matters.
Unknown JSON fields are dropped, and nothing says so
By default the gateway unmarshals JSON with protojson configured asDiscardUnknown: true. A field that the target message does not define is
removed during transcoding and the request continues as if it had never been
sent.
Given this service:
message Command {
string action = 1;
int32 amount = 2;
}
service Gateway {
rpc Send(Command) returns (Ack) {
option (google.api.http) = {
post: "/v1/send"
body: "*"
};
}
}a request carrying a third field:
POST /v1/send {"action":"transfer","amount":1,"auth":"..."}reaches the backend as action and amount only. The auth field is gone,
the backend sees a well formed message, and the client gets 200 OK. Neither
end is told that anything was removed.
This is the behaviour the proto3 JSON mapping describes, and other JSON to gRPC
transcoders default the same way, so a client that works against one of them
will behave the same here. It is worth stating plainly because it is more
lenient than the protojson library the gateway wraps, which rejects unknown
fields by default.
When this matters
The case to watch for is a client that puts something in the request expecting
the server to act on it, where the field is not in the schema. A token added by
an intermediary, a tenant identifier, a signature over the body, a flag a newer
client sends to an older backend. All of these disappear quietly, and the
request still succeeds, so the failure does not look like a failure.
It also matters when the schema changes. Removing or renaming a field does not
break clients that still send it. That is often the point, but it means a
rename can silently stop delivering data that the backend used to receive.
Rejecting unknown fields instead
Set DiscardUnknown to false on the marshaler:
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: false,
},
}),
)The same request now returns 400 with unknown field "auth".
This is a good default for an internal API where every client is yours and a
stray field means someone made a mistake. It is a poor default for a public API
where clients you do not control may send extra fields for reasons of their
own, since it turns forward compatibility into an error.
Noticing without rejecting
If rejecting is too strict but silence is too quiet, you can unmarshal twice and
log the difference: once with DiscardUnknown: false to see whether the strict
parse would have failed, then with the permissive setting for the request that
actually proceeds. That costs an extra parse per request, so it suits a
canary or a sampled fraction of traffic rather than everything.
See also
- Customizing your gateway, which shows
where the marshaler options go. - Custom marshalers.
Docs Operations Tracing (`docs/docs/operations/tracing.md`)
layout: default title: Tracing nav_order: 2 parent: Operations
Tracing
If you are starting a new project, jump to the OpenTelemetry
section below. OpenTelemetry is the current standard for tracing in the Go
ecosystem and, when combined with otelhttp and otelgrpc, propagates spans
through the gateway with no custom middleware. The
OpenCensus and
OpenTracing sections below are kept for existing
integrations and are considered legacy.
With [OpenCensus.io](https://opencensus.io/) and [AWS X-ray](https://aws.amazon.com/xray/)
Legacy: OpenCensus is deprecated in favor of OpenTelemetry. See the
OpenTelemetry section for the current approach.
Adding tracing using AWS-Xray as the exporter
This example uses the AWS-Xray exporter with a global trace setting. Note that AWS X-ray exporter does not handle any metrics only tracing.
- Add the following imports
xray "contrib.go.opencensus.io/exporter/aws"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/trace"- Register the AWS X-ray exporter for the GRPC server
xrayExporter, err := xray.NewExporter(
xray.WithVersion("latest"),
// Add your AWS region.
xray.WithRegion("ap-southeast-1"),
)
if err != nil {
// Handle any error.
}
// Do not forget to call Flush() before the application terminates.
defer xrayExporter.Flush()
// Register the trace exporter.
trace.RegisterExporter(xrayExporter)- Add a global tracing configuration
// Always trace in this example.
// In production this can be set to a trace.ProbabilitySampler.
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})- Add
ocgrpc.ClientHandlerfor tracing the gRPC client calls
conn, err := grpc.NewClient(
// Other options goes here.
// Add ocgrpc.ClientHandler for tracing the grpc client calls.
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
)- Wrap the gateway mux with the OpenCensus HTTP handler
gwmux := runtime.NewServeMux()
openCensusHandler := &ochttp.Handler{
Handler: gwmux,
}
gwServer := &http.Server{
Addr: "0.0.0.0:10000",
Handler: openCensusHandler,
}),
}Without a global configuration
In this example we have added the gRPC Health Checking Protocol and we do not wish to trace any health checks.
Follow step
1,2and4from the previous section.Since we are not using a global configuration we can decide what paths we want to trace.
gwmux := runtime.NewServeMux()
openCensusHandler := &ochttp.Handler{
Handler: gwmux,
GetStartOptions: func(r *http.Request) trace.StartOptions {
startOptions := trace.StartOptions{}
if strings.HasPrefix(r.URL.Path, "/api") {
// This example will always trace anything starting with /api.
startOptions.Sampler = trace.AlwaysSample()
}
return startOptions
},
}- No global configuration means we have to use the per span sampler.
A method we want to trace
func (s *service) Name(ctx context.Context, req *pb.Request) (*pb.Response, error) {
// Here we add the span ourselves.
ctx, span := trace.StartSpan(ctx, "name.to.use.in.trace", trace.
// Select a sampler that fits your implementation.
WithSampler(trace.AlwaysSample()))
defer span.End()
/// Other stuff goes here.
}A method we do not wish to trace
func (s *service) Check(ctx context.Context, in *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
// Note no span here.
return &health.HealthCheckResponse{Status: health.HealthCheckResponse_SERVING}, nil
}OpenTracing Support (legacy)
Legacy: OpenTracing is deprecated in favor of OpenTelemetry. The custom
tracingWrappermiddleware below is only needed for OpenTracing. With
OpenTelemetry'sotelhttpandotelgrpcno such wrapper is required — see the
OpenTelemetry section.
If your project uses OpenTracing and you'd like spans to propagate through the gateway, you can add some middleware which parses the incoming HTTP headers to create a new span correctly.
import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
var grpcGatewayTag = opentracing.Tag{Key: string(ext.Component), Value: "grpc-gateway"}
func tracingWrapper(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
parentSpanContext, err := opentracing.GlobalTracer().Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header))
if err == nil || err == opentracing.ErrSpanContextNotFound {
serverSpan := opentracing.GlobalTracer().StartSpan(
"ServeHTTP",
// this is magical, it attaches the new span to the parent parentSpanContext, and creates an unparented one if empty.
ext.RPCServerOption(parentSpanContext),
grpcGatewayTag,
)
r = r.WithContext(opentracing.ContextWithSpan(r.Context(), serverSpan))
defer serverSpan.Finish()
}
h.ServeHTTP(w, r)
})
}
// Then just wrap the mux returned by runtime.NewServeMux() like this
if err := http.ListenAndServe(":8080", tracingWrapper(mux)); err != nil {
log.Fatalf("failed to start gateway server on 8080: %v", err)
}Finally, don't forget to add a tracing interceptor when registering
the services. E.g.
import (
"google.golang.org/grpc"
"github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
)
opts := []grpc.DialOption{
grpc.WithUnaryInterceptor(
grpc_opentracing.UnaryClientInterceptor(
grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
),
),
}
if err := pb.RegisterMyServiceHandlerFromEndpoint(ctx, mux, serviceEndpoint, opts); err != nil {
log.Fatalf("could not register HTTP service: %v", err)
}OpenTelemetry
OpenTelemetry is the recommended way to trace
requests through the gateway. You do not need a custom tracingWrapper (as in
the legacy OpenTracing section above). The official instrumentation libraries
already do the work:
otelhttp
wraps the gatewayhttp.Handler, extracts the incoming
W3C Trace Context from the request
headers, and starts the HTTP server span.otelgrpc
stats handlers start the gRPC client span for the gateway's outgoing call
(injecting the trace context into the gRPC metadata) and the gRPC server
span on the backend.
An incoming HTTP request therefore produces three connected parent/child spans
that share a single trace ID:
HTTP server span (otelhttp) -> gRPC client span (otelgrpc) -> gRPC server span (otelgrpc)Runnable example
A complete, self-contained example lives atexamples/internal/cmd/example-otel-tracing.
In a single process it wires up a stdout span exporter, an instrumented
in-process gRPC backend, and the gateway. Run it with:
go run ./examples/internal/cmd/example-otel-tracing
# then, in another terminal:
curl http://localhost:8081/say/OpenTelemetryWiring
Install a TracerProvider and a propagator once at startup:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
if err != nil {
// Handle any error.
}
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
defer tp.Shutdown(context.Background())
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))Instrument the gRPC backend with the server stats handler:
import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
s := grpc.NewServer(grpc.StatsHandler(otelgrpc.NewServerHandler()))Dial the backend with the client stats handler so the gateway's outgoing call is
traced and the trace context is propagated:
conn, err := grpc.NewClient(
"localhost:9091",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)Finally, wrap the gateway mux with otelhttp — this replaces the customtracingWrapper entirely:
import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
mux := runtime.NewServeMux()
if err := pb.RegisterGreeterHandler(ctx, mux, conn); err != nil {
// Handle any error.
}
handler := otelhttp.NewHandler(mux, "grpc-gateway")
if err := http.ListenAndServe(":8081", handler); err != nil {
log.Fatalf("failed to start gateway server: %v", err)
}Sample output
Issuing curl http://localhost:8081/say/OpenTelemetry prints three spans that
all share the same TraceID, confirming the trace propagates through the
gateway. Trimmed for brevity:
// 1. HTTP server span (otelhttp) — the root span.
{
"Name": "GET",
"SpanContext": { "TraceID": "6952...4aa5", "SpanID": "3b9c...fd0e" },
"Parent": { "TraceID": "0000...0000", "SpanID": "0000...0000" },
"SpanKind": 2, // server
"InstrumentationScope": { "Name": ".../net/http/otelhttp" }
}
// 2. gRPC client span (otelgrpc) — child of the HTTP span.
{
"Name": "grpc.gateway.examples.internal.helloworld.Greeter/SayHello",
"SpanContext": { "TraceID": "6952...4aa5", "SpanID": "1224...75ca" },
"Parent": { "TraceID": "6952...4aa5", "SpanID": "3b9c...fd0e" },
"SpanKind": 3, // client
"InstrumentationScope": { "Name": ".../grpc/otelgrpc" }
}
// 3. gRPC server span (otelgrpc) — child of the client span, context received over the wire.
{
"Name": "grpc.gateway.examples.internal.helloworld.Greeter/SayHello",
"SpanContext": { "TraceID": "6952...4aa5", "SpanID": "950f...d85e" },
"Parent": { "TraceID": "6952...4aa5", "SpanID": "1224...75ca", "Remote": true },
"SpanKind": 2, // server
"InstrumentationScope": { "Name": ".../grpc/otelgrpc" }
}Docs Overview Background (`docs/docs/overview/background.md`)
layout: default title: Background nav_order: 0 parent: Overview
Background
gRPC is great -- it generates API clients and server stubs in many programming languages, it is fast, easy-to-use, bandwidth-efficient and its design is combat-proven by Google. However, you might still want to provide a traditional RESTful API as well. Reasons can range from maintaining backwards-compatibility, supporting languages or clients not well supported by gRPC to simply maintaining the aesthetics and tooling involved with a RESTful architecture.
This project aims to provide that HTTP+JSON interface to your gRPC service. A small amount of configuration in your service to attach HTTP semantics is all that's needed to generate a reverse-proxy with this library.
Docs Overview Usage (`docs/docs/overview/usage.md`)
layout: default title: How do I use this? nav_order: 1 parent: Overview
How do I use this?
Follow the instructions in the README.
Docs Tutorials Adding Annotations (`docs/docs/tutorials/adding_annotations.md`)
layout: default title: Adding gRPC-Gateway annotations to an existing proto file nav_order: 4 parent: Tutorials
Adding gRPC-Gateway annotations to an existing proto file
Now that we've got a working Go gRPC server, we need to add the gRPC-Gateway annotations.
The annotations define how gRPC services map to the JSON request and response. When using protocol buffers, each RPC must define the HTTP method and path using the google.api.http annotation.
So we will need to add the google/api/http.proto import to the proto file. We also need to add the HTTP->gRPC mapping we want. In this case, we're mapping POST /v1/example/echo to our SayHello RPC.
syntax = "proto3";
package helloworld;
import "google/api/annotations.proto";
// Here is the overall greeting service definition where we define all our endpoints
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
// The request message containing the user's name
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}See a_bit_of_everything.proto for examples of more annotations you can add to customize gateway behavior.
Generating the gRPC-Gateway stubs
Now that we've got the gRPC-Gateway annotations added to the proto file, we need to use the gRPC-Gateway generator to generate the stubs.
Using buf
We'll need to add the gRPC-Gateway generator to the generation configuration:
version: v1
plugins:
- plugin: go
out: proto
opt: paths=source_relative
- plugin: go-grpc
out: proto
opt: paths=source_relative,require_unimplemented_servers=false
- plugin: grpc-gateway
out: proto
opt: paths=source_relativeWe'll also need to add the googleapis dependency to our buf.yaml file:
version: v1
name: buf.build/myuser/myrepo
deps:
- buf.build/googleapis/googleapisThen we need to run buf mod update to select a version of the dependency to use.
And that's it! Now if you run:
$ buf generateIt should produce a *.gw.pb.go file.
Using `protoc`
Before we can generate the stubs with protoc, we need to copy some dependencies into our proto file structure. Copy a subset of the googleapis
from the official repository to your local proto file structure. It should look like this afterwards:
proto
├── google
│ └── api
│ ├── annotations.proto
│ └── http.proto
└── helloworld
└── hello_world.protoNow we need to add the gRPC-Gateway generator to the protoc invocation:
$ protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
--grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
./proto/helloworld/hello_world.protoThis should generate a *.gw.pb.go file.
We also need to add and serve the gRPC-Gateway mux in our main.go file.
/* Detailed source-code truncated for AI context efficiency. */For more examples, please refer to our boilerplate repository.
Testing the gRPC-Gateway
Now we can start the server:
$ go run main.goThen we use cURL to send HTTP requests:
$ curl -X POST -k http://localhost:8090/v1/example/echo -d '{"name": " hello"}'{"message":"hello world"}Hopefully, that gives a bit of understanding of how to use the gRPC-Gateway.
Full source code of hello world program can be found here helloworld-grpc-gateway.
Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
Docs Tutorials Creating Main.Go (`docs/docs/tutorials/creating_main.go.md`)
layout: default title: Creating main.go nav_order: 3 parent: Tutorials
Creating main.go
Before creating main.go file we are assuming that the user has created a go.mod with the name github.com/myuser/myrepo, if not please refer to Creating go.mod file. The import here is using the path to the generated files in proto/helloworld relative to the root of the repository.
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
helloworldpb "github.com/myuser/myrepo/proto/helloworld"
)
type server struct{
helloworldpb.UnimplementedGreeterServer
}
func NewServer() *server {
return &server{}
}
func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil
}
func main() {
// Create a listener on TCP port
lis, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalln("Failed to listen:", err)
}
// Create a gRPC server object
s := grpc.NewServer()
// Attach the Greeter service to the server
helloworldpb.RegisterGreeterServer(s, &server{})
// Serve gRPC Server
log.Println("Serving gRPC on 0.0.0.0:8080")
log.Fatal(s.Serve(lis))
}Read More
For more refer to gRPC docs https://grpc.io/docs/languages/go/.
Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
Docs Tutorials Introduction (`docs/docs/tutorials/introduction.md`)
layout: default title: Introduction to the gRPC-Gateway nav_order: 0 parent: Tutorials
Introduction to the gRPC-Gateway
We all know that gRPC is not a tool for everything. There are cases where we still want to provide a traditional HTTP/JSON API. The reasons can range from maintaining backward-compatibility to supporting programming languages or clients not well supported by gRPC. But writing another service just to expose an HTTP/JSON API is quite a time consuming and tedious task.
So is there any way to code just once, but provide APIs in both gRPC and HTTP/JSON at the same time?
The answer is Yes.
The gRPC-Gateway is a plugin of the Google protocol buffers compiler protoc. It reads protobuf service definitions and generates a reverse-proxy server which translates a RESTful HTTP API into gRPC. This server is generated according to the google.api.http annotations in your service definitions.
This helps you provide your APIs in both gRPC and HTTP/JSON format at the same time.
Prerequisites
Before we start coding, we have to install some tools.
We will be using a Go gRPC server in the examples, so please install Go first from https://golang.org/dl/.
After installing Go, use go install to download and build the following binaries:
$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latestThis installs the protoc generator plugins we need to generate the stubs. Make sure to add $GOPATH/bin to your $PATH so that executables installed via go get are available on your $PATH.
We will be working in a new module for this tutorial, so go ahead and create that in a folder of your choosing now:
Creating go.mod file
Start your module using the go mod init command to create a go.mod file.
Run the go mod init command, giving it the path of the module your code will be in. Here, use github.com/myuser/myrepo for the module path -- in production code, this would be the URL from which your module can be downloaded.
$ go mod init github.com/myuser/myrepo
go: creating new go.mod: module github.com/myuser/myrepoThe go mod init command creates a go.mod file that identifies your code as a module that might be used from other code. The file you just created includes only the name of your module and the Go version your code supports. But as you add dependencies -- meaning packages from other modules -- the go.mod file will list the specific module versions to use. This keeps builds reproducible and gives you direct control over which module versions to use.
Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
Docs Tutorials Learn More (`docs/docs/tutorials/learn_more.md`)
layout: default title: Learn More nav_order: 5 parent: Tutorials
Learn More
How it works
When the HTTP request arrives at the gRPC-Gateway, it parses the JSON data into a protobuf message. It then makes a normal Go gRPC client request using the parsed protobuf message. The Go gRPC client encodes the protobuf structure into the protobuf binary format and sends it to the gRPC server. The gRPC Server handles the request and returns the response in the protobuf binary format. The Go gRPC client parses it into a protobuf message and returns it to the gRPC-Gateway, which encodes the protobuf message to JSON and returns it to the original client.
google.api.http
Read more about google.api.http in the source file documentation.
HTTP and gRPC Transcoding
Read more about HTTP and gRPC Transcoding on AIP 127.
Docs Tutorials Simple Hello World (`docs/docs/tutorials/simple_hello_world.md`)
layout: default title: Creating a simple hello world with gRPC nav_order: 1 parent: Tutorials
Creating a simple hello world with gRPC
To understand the gRPC-Gateway we are going to first make a hello world gRPC service.
Defining your gRPC service using protocol buffers
Before we create a gRPC service, we should create a proto file to define what we need, here we create a file named hello_world.proto in the directory proto/helloworld/hello_world.proto.
The gRPC service is defined using Google Protocol Buffers. To learn more about how to define a service in a .proto file see their Basics tutorial. For now, all you need to know is that both the server and the client stub have a SayHello() RPC method that takes a HelloRequest parameter from the client and returns a HelloReply from the server, and that the method is defined like this:
syntax = "proto3";
package helloworld;
// The greeting service definition
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
Docs Tutorials Generating Stubs Using Buf (`docs/docs/tutorials/generating_stubs/using_buf.md`)
layout: default title: Generating stubs using buf nav_order: 0 parent: Generating stubs grand_parent: Tutorials
Generating stubs using buf
Buf is a tool that provides various protobuf utilities such as linting, breaking change detection and generation. Please find installation instructions on https://docs.buf.build/installation/.
It is configured through a buf.yaml file that should be checked in to the root of your Protobuf file hierarchy. Buf will automatically read this file if present. Configuration can also be provided via the command-line flag --config, which accepts a path to a .json or .yaml file, or direct JSON or YAML data. As opposed to protoc, where all .proto files are manually specified on the command-line, buf operates by recursively discovering all .proto files under configuration and building them.
The following is an example of a valid configuration, and you would put it in the root of your Protobuf file hierarchy, e.g. in proto/buf.yaml relative to the root of your repository.
version: v1
name: buf.build/myuser/myrepoTo generate type and gRPC stubs for Go, create the file buf.gen.yaml:
version: v1
plugins:
- plugin: go
out: proto
opt: paths=source_relative
- plugin: go-grpc
out: proto
opt: paths=source_relativeWe use the go and go-grpc plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the proto folder, and we're using the paths=source_relative option, which means that the generated files will appear in the same directory as the source .proto file.
Then run
$ buf generateThis will have generated a *.pb.go and a *_grpc.pb.go file for each protobuf package in our proto file hierarchy.
Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
Docs Tutorials Generating Stubs Using Protoc (`docs/docs/tutorials/generating_stubs/using_protoc.md`)
layout: default title: Generating stubs using protoc nav_order: 1 parent: Generating stubs grand_parent: Tutorials
Generating stubs using protoc
Here's an example of what a protoc command might look like to generate Go stubs, assuming that you're at the root of your repository and you have your proto files in a directory called proto:
$ protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
./proto/helloworld/hello_world.protoWe use the go and go-grpc plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the proto folder, and we're using the paths=source_relative option, which means that the generated files will appear in the same directory as the source .proto file.
This will have generated a *.pb.go and a *_grpc.pb.go file for proto/helloworld/hello_world.proto.
Next{: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
--- METRICS ---
- Files Extracted: 48
- Estimated Token Budget: ~50218 tokens
- Canonical Reference: https://codewiki.google/github.com/grpc-ecosystem/grpc-gateway