gRPC Basics
10 examples to get you started with gRPC & Protobuf - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with gRPC & Protobuf - 7 basic and 3 intermediate.
protoc (Protocol Buffers compiler) from protobuf releases.go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest$GOPATH/bin or $(go env GOPATH)/bin is on your PATH.mkdir greet && cd greet && go mod init example.com/greet.go get google.golang.org/grpc@latest.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;
}proto3 is the default syntax for new gRPC APIs.go_package sets the Go import path and package name for generated code.Related: Protocol Buffers Schema Design - field rules
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_out emits message structs (greet.pb.go).--go-grpc_out emits service interfaces (greet_grpc.pb.go).paths=source_relative keeps generated files beside the .proto.Related: gRPC in Go: Contracts, Streaming, and Performance - contract model
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
}Unimplemented… so new RPCs do not break compiles.GetName() accessors - they handle nil messages safely.(nil, err) for failures; gRPC maps errors to status codes.Related: gRPC Error Codes & Status Mapping - return errors
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))
}50051 is conventional for examples; use config in production.grpc.NewServer() accepts server options (TLS, interceptors) covered in advanced pages.Serve blocks until the process exits or GracefulStop runs.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.NewClient is the modern dial API (replaces deprecated Dial patterns).Close() connections on shutdown to release HTTP/2 resources.insecure.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())
}status.FromError.Related: gRPC Middleware, Interceptors & Metadata - metadata and interceptors
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.InvalidArgument signals client mistakes; use codes.Internal for server bugs.fmt.Errorf becomes Unknown unless you attach status with status.Errorf.status.Code(err), not string matching.Related: gRPC Error Codes & Status Mapping - code mapping
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 list discovers services at runtime.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()
}
}GracefulStop waits for active RPCs; Stop forces immediate teardown.preStop hooks and shortened terminationGracePeriodSeconds.Unavailable during rolling deploys.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)
}grpc.health.v1.Health/Check.NOT_SERVING during dependency warmup before marking the pod ready.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).
Reviewed by Chris St. John·Last updated Jul 18, 2026