gRPC Basics
10 examples to get you started with gRPC & Protobuf - 7 basic and 3 intermediate.
Prerequisites
- Install Go 1.26.x from go.dev/dl.
- Install
protoc(Protocol Buffers compiler) from protobuf releases. - Install Go plugins:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest- Ensure
$GOPATH/binor$(go env GOPATH)/binis on yourPATH. - Create a module:
mkdir greet && cd greet && go mod init example.com/greet. - Add gRPC:
go get google.golang.org/grpc@latest.
Basic Examples
1. Minimal proto file
Define a service and messages in api/greet/v1/greet.proto.
syntax = "proto3";
package greet.v1;
option go_package = "example.com/greet/api/greet/v1;greetv1";
service Greeter {
rpc SayHello(HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}proto3is the default syntax for new gRPC APIs.- Field numbers are permanent identifiers - never reuse them.
go_packagesets the Go import path and package name for generated code.
Related: Protocol Buffers Schema Design - field rules
2. Generate Go stubs
Run protoc from the module root.
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
api/greet/v1/greet.proto--go_outemits message structs (greet.pb.go).--go-grpc_outemits service interfaces (greet_grpc.pb.go).paths=source_relativekeeps generated files beside the.proto.
Related: gRPC in Go: Contracts, Streaming, and Performance - contract model
3. Implement the server interface
Embed UnimplementedGreeterServer for forward compatibility.
package main
import (
"context"
greetv1 "example.com/greet/api/greet/v1"
)
type greeterServer struct {
greetv1.UnimplementedGreeterServer
}
func (s *greeterServer) SayHello(ctx context.Context, req *greetv1.HelloRequest) (*greetv1.HelloResponse, error) {
return &greetv1.HelloResponse{Message: "hello, " + req.GetName()}, nil
}- Generated servers require embedding
Unimplemented…so new RPCs do not break compiles. - Use
GetName()accessors - they handle nil messages safely. - Return
(nil, err)for failures; gRPC maps errors to status codes.
Related: gRPC Error Codes & Status Mapping - return errors
4. Start a gRPC listener
Register the service and block on Serve.
package main
import (
"log"
"net"
greetv1 "example.com/greet/api/greet/v1"
"google.golang.org/grpc"
)
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatal(err)
}
s := grpc.NewServer()
greetv1.RegisterGreeterServer(s, &greeterServer{})
log.Println("listening on :50051")
log.Fatal(s.Serve(lis))
}- Default port
50051is conventional for examples; use config in production. grpc.NewServer()accepts server options (TLS, interceptors) covered in advanced pages.Serveblocks until the process exits orGracefulStopruns.
5. Dial a client connection
Create an insecure client for local dev.
package main
import (
"log"
greetv1 "example.com/greet/api/greet/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
conn, err := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := greetv1.NewGreeterClient(conn)
_ = client // use in next example
}grpc.NewClientis the modern dial API (replaces deprecatedDialpatterns).- Always
Close()connections on shutdown to release HTTP/2 resources. - Production uses TLS credentials, not
insecure.
6. Unary RPC call with context
Pass a context for deadlines and cancellation.
package main
import (
"context"
"fmt"
"log"
"time"
greetv1 "example.com/greet/api/greet/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
conn, err := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
client := greetv1.NewGreeterClient(conn)
resp, err := client.SayHello(ctx, &greetv1.HelloRequest{Name: "world"})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.GetMessage())
}- Context propagates deadlines to the server automatically.
- Client-side errors wrap gRPC status codes inspectable with
status.FromError. - Timeouts prevent hung calls when backends stall.
Related: gRPC Middleware, Interceptors & Metadata - metadata and interceptors
7. Return a gRPC status error
Use status.Error for typed failures.
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func validateName(name string) error {
if name == "" {
return status.Error(codes.InvalidArgument, "name is required")
}
return nil
}codes.InvalidArgumentsignals client mistakes; usecodes.Internalfor server bugs.- Plain
fmt.ErrorfbecomesUnknownunless you attach status withstatus.Errorf. - Clients should branch on
status.Code(err), not string matching.
Related: gRPC Error Codes & Status Mapping - code mapping
Intermediate Examples
8. Server reflection for grpcurl
Enable reflection to introspect services without proto files on disk.
import "google.golang.org/grpc/reflection"
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
greetv1.RegisterGreeterServer(s, &greeterServer{})
reflection.Register(s)
s.Serve(lis)
}grpcurl -plaintext localhost:50051 listdiscovers services at runtime.- Disable reflection in production unless policy allows - it exposes your API surface.
- Useful for staging debugging and contract smoke tests.
9. Graceful shutdown
Stop accepting new RPCs while finishing in-flight work.
import (
"os"
"os/signal"
"syscall"
"time"
)
func serveWithGracefulStop(s *grpc.Server, lis net.Listener) {
go func() {
if err := s.Serve(lis); err != nil {
log.Fatal(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
stopped := make(chan struct{})
go func() {
s.GracefulStop()
close(stopped)
}()
select {
case <-stopped:
case <-time.After(10 * time.Second):
s.Stop()
}
}GracefulStopwaits for active RPCs;Stopforces immediate teardown.- Pair with Kubernetes
preStophooks and shortenedterminationGracePeriodSeconds. - Clients should retry
Unavailableduring rolling deploys.
10. Health check service
Register the standard gRPC health protocol for probes.
import (
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
func main() {
s := grpc.NewServer()
greetv1.RegisterGreeterServer(s, &greeterServer{})
healthServer := health.NewServer()
healthpb.RegisterHealthServer(s, healthServer)
healthServer.SetServingStatus("greet.v1.Greeter", healthpb.HealthCheckResponse_SERVING)
// ... Serve(lis)
}- Kubernetes gRPC health probes call
grpc.health.v1.Health/Check. - Set
NOT_SERVINGduring dependency warmup before marking the pod ready. - Service name must match what your probe configuration expects.
Related: gRPC Best Practices - deadlines, versioning, K8s
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).