To build a Connect server, follow these steps:
- Define your service in a
.proto schema. - Generate the Connect code.
- Implement the service interface by embedding the generated
Unimplemented<Service>Handler struct. - Register the handler using the generated constructor (e.g.,
New<Service>Handler) with an http.ServeMux. - Use
connect.WithInterceptors to add middleware like validation.
Note: For production, refer to deployment guides for configuring timeouts, connection pools, and observability.
package main
import (
"context"
"log"
"net/http"
pingv1 "connectrpc.com/connect/internal/gen/connect/ping/v1"
pingv1connect "connectrpc.com/connect/internal/gen/simple/connect/ping/v1/pingv1connect"
"connectrpc.com/connect"
"connectrpc.com/validate"
)
type PingServer struct {
pingv1connect.UnimplementedPingServiceHandler // returns errors from all methods
}
func (ps *PingServer) Ping(ctx context.Context, req *pingv1.PingRequest) (*pingv1.PingResponse, error) {
return &pingv1.PingResponse{
Number: req.Number,
}, nil
}
func main() {
mux := http.NewServeMux()
// The generated constructors return a path and a plain net/http
// handler.
mux.Handle(
pingv1connect.NewPingServiceHandler(
&PingServer{},
// Validation via Protovalidate is almost always recommended
connect.WithInterceptors(validate.NewInterceptor()),
),
)
p := new(http.Protocols)
p.SetHTTP1(true)
// For gRPC clients, it's convenient to support HTTP/2 without TLS.
p.SetUnencryptedHTTP2(true)
s := &http.Server{
Addr: "localhost:8080",
Handler: mux,
Protocols: p,
}
if err := s.ListenAndServe(); err != nil {
log.Fatalf("listen failed: %v", err)
}
}