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:

  1. import "protoc-gen-openapiv2/options/annotations.proto";

instead of

  1. import "protoc-gen-swagger/options/annotations.proto";

and

  1. option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {

instead of

  1. 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:

  1. example: { value: '{ "uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814" }' }

It would now look like this:

  1. 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:

  1. mux := runtime.NewServeMux(
  2. runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
  3. Marshaler: &runtime.JSONPb{
  4. MarshalOptions: protojson.MarshalOptions{
  5. UseProtoNames: true,
  6. EmitUnpopulated: true,
  7. },
  8. UnmarshalOptions: protojson.UnmarshalOptions{
  9. DiscardUnknown: true,
  10. },
  11. },
  12. }),
  13. )

To change the OpenAPI generator behaviour to match, set json_names_for_fields=false when generating:

  1. --openapiv2_out=json_names_for_fields=false:./gen/openapiv2 path/to/my/proto/v1/myproto.proto

If 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 with EmitUnpopulated: false:

  1. mux := runtime.NewServeMux(
  2. runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
  3. Marshaler: &runtime.JSONPb{
  4. MarshalOptions: protojson.MarshalOptions{
  5. EmitUnpopulated: false,
  6. },
  7. UnmarshalOptions: protojson.UnmarshalOptions{
  8. DiscardUnknown: true,
  9. },
  10. },
  11. }),
  12. )

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:

  1. mux := runtime.NewServeMux(
  2. runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
  3. MarshalOptions: protojson.MarshalOptions{
  4. EmitUnpopulated: true,
  5. },
  6. UnmarshalOptions: protojson.UnmarshalOptions{
  7. DiscardUnknown: true,
  8. },
  9. }),
  10. )

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:

  1. mux := runtime.NewServeMux(
  2. runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
  3. Marshaler: &runtime.JSONPb{
  4. MarshalOptions: protojson.MarshalOptions{
  5. EmitUnpopulated: true,
  6. },
  7. UnmarshalOptions: protojson.UnmarshalOptions{
  8. DiscardUnknown: false,
  9. },
  10. },
  11. }),
  12. )

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.