Skip to content

vallahaye/connect-gateway

Repository files navigation

Connect-Gateway

Go Reference Go Report Card

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.

Features

  • Unary calls support
  • Connect interceptors support
  • Bidirectional gRPC metadata transmission
  • Connect errors to gRPC errors convertion

Limitations

Getting started

Install tools

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

Generate code

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

Implement handler

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)
}