docs/go/how-to/grpc-connect.md
The Connect protocol is an HTTP/2-based protocol for RPC communication. It's conceptually similar to gRPC, but with better support for using from browsers and JavaScript clients.
This guide shows how to use Encore for setting up a Connect service for external clients to use:
We'll largely follow the connect-go getting started guide with some small tweaks.
Start by installing the necessary tools:
$ go install github.com/bufbuild/buf/cmd/buf@latest
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
Next, inside your Encore application (create one if you haven't already)
create a new file at greet/v1/greet.proto with the following contents:
-- greet/v1/greet.proto --
syntax = "proto3";
package greet.v1;
option go_package = "encore.app/gen/greet/v1;greetv1";
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
service GreetService {
rpc Greet(GreetRequest) returns (GreetResponse) {}
}
Next, add a buf.gen.yaml in the repository root, containing:
-- buf.gen.yaml --
version: v2
plugins:
- local: protoc-gen-go
out: gen
opt: paths=source_relative
- local: protoc-gen-connect-go
out: gen
opt: paths=source_relative
Now it's time to generate the connect-go service code. Run:
$ buf lint
$ buf generate
If all went well, you should see a new gen directory in the repository root containing some generated Go code:
gen
└── greet
└── v1
├── greet.pb.go
└── greetv1connect
└── greet.connect.go
Now that we have the service definition, we can implement the Connect service in Go.
Add the file greet/greet.go with the following contents:
-- greet/greet.go --
package greet
import (
"context"
"fmt"
"log"
"connectrpc.com/connect"
greetv1 "encore.app/gen/greet/v1" // generated by protoc-gen-go
)
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
}
The sample code is straight from the getting started guide; there are no Encore specific changes required here.
</Callout>Now we'll create an Encore service struct that initializes the Connect service, and a raw endpoint that forwards incoming requests to the Connect service.
Add the file greet/service.go with the following contents:
-- greet/service.go --
package greet
import (
"net/http"
"encore.app/gen/greet/v1/greetv1connect"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
//encore:service
type Service struct {
routes http.Handler
}
//encore:api public raw path=/greet.v1.GreetService/*endpoint
func (s *Service) GreetService(w http.ResponseWriter, req *http.Request) {
s.routes.ServeHTTP(w, req)
}
func initService() (*Service, error) {
greeter := &GreetServer{}
mux := http.NewServeMux()
path, handler := greetv1connect.NewGreetServiceHandler(greeter)
mux.Handle(path, handler)
return &Service{routes: mux}, nil
}
That's it! We're ready to run the service and check that everything works.
Run the service with encore run:
$ encore run
Once it starts up, open a separate terminal and use grpcurl to call the service:
# Install grpcurl if you haven't already
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# Call the service with grpcurl
grpcurl \
-protoset <(buf build -o -) -plaintext \
-d '{"name": "Jane"}' \
localhost:4000 greet.v1.GreetService/Greet
{"greeting": "Hello, Jane!"}
# Or call the service with curl
$ curl -H "Content-Type: application/json" -d '{"name": "Jane"}' http://localhost:4000/greet.v1.GreetService/Greet
{"greeting":"Hello, Jane!"} # Expected response
If you see {"greeting":"Hello, Jane!"}, everything is working!
What's more, Encore automatically traces the incoming requests, and adds request logging and captures request metrics.