The Connect-Gateway introduces direct binding from gRPC-Gateway local handlers to Connect service handlers. It addresses the recurring request to support Google API HTTP annotations in Connect:
We provide a complete solution for the two to communicate seamlessly through simple function calls, without relying on network communications. All of which is done by reusing as much of the code from both projects as possible and mimicking the connect-go implementation, i.e. reducing code generation as much as possible, with most of the logic being provided in a library.
- Unary calls support
- Connect interceptors support
- Bidirectional gRPC metadata transmission
- Connect errors to gRPC errors convertion
- No support for streaming calls as it is not yet supported by the gRPC-Gateway's "in-process" transport mode
- Uninitialized Request.Peer and Request.Spec properties on Connect requests as it cannot be set externally
First, we'll need to install the protoc-gen-connect-gateway
code generation tool:
$ go install go.vallahaye.net/connect-gateway/cmd/protoc-gen-connect-gateway@latest
Make sure that the binary is accessible from your PATH
:
$ which protoc-gen-connect-gateway
/go/bin/protoc-gen-connect-gateway
Assuming Buf code generation is already configured for Protocol Buffers, gRPC, the gRPC-Gateway and Connect, add the Connect-Gateway plugin to your buf.gen.yaml
file:
version: v2
plugins:
- local: protoc-gen-connect-gateway
out: gen
opt: paths=source_relative
Then run the buf generate
command to generate code.
Applying this configuration to the Connect Greet service example produces the following output, where the greet.connect.gw.go
file contains the adapter to interface the gRPC gateway with the Connect server handlers for the GreetService
:
gen
└── greet
└── v1
├── greet_grpc.pb.go
├── greet.pb.go
├── greet.pb.gw.go
└── greetv1connect
├── greet.connect.go
└── greet.connect.gw.go
We use the code generated by instantiating a gRPC-Gateway router and registering Connect server handlers with it, using the helper function generated by the Connect-Gateway plugin:
package main
import (
"context"
"fmt"
"log"
"net/http"
"connectrpc.com/connect"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
greetv1 "example/gen/greet/v1"
"example/gen/greet/v1/greetv1connect"
)
type GreetServer struct{}
func (s *GreetServer) Greet(
ctx context.Context,
req *connect.Request[greetv1.GreetRequest],
) (*connect.Response[greetv1.GreetResponse], error) {
log.Println("Request headers: ", req.Header())
res := connect.NewResponse(&greetv1.GreetResponse{
Greeting: fmt.Sprintf("Hello, %s!", req.Msg.Name),
})
res.Header().Set("Greet-Version", "v1")
return res, nil
}
func main() {
greeter := &GreetServer{}
mux := runtime.NewServeMux()
greetv1connect.RegisterGreetServiceHandlerGatewayServer(mux, greeter)
http.ListenAndServe(":8080", mux)
}