Claude-skill-registry go-grpc
Build gRPC services with Go - protobuf, streaming, interceptors
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-grpc" ~/.claude/skills/majiayu000-claude-skill-registry-go-grpc && rm -rf "$T"
manifest:
skills/data/go-grpc/SKILL.mdsource content
Go gRPC Skill
Build high-performance gRPC services with Go.
Overview
Complete guide for gRPC development including protobuf design, streaming, interceptors, and error handling.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| streaming | string | no | "unary" | Type: "unary", "server", "client", "bidi" |
| auth | string | no | "none" | Auth: "none", "jwt", "mtls" |
Core Topics
Protobuf Definition
syntax = "proto3"; package api.v1; option go_package = "github.com/example/api/v1;apiv1"; service UserService { rpc GetUser(GetUserRequest) returns (GetUserResponse); rpc ListUsers(ListUsersRequest) returns (stream User); rpc CreateUser(CreateUserRequest) returns (CreateUserResponse); } message User { int64 id = 1; string name = 2; string email = 3; google.protobuf.Timestamp created_at = 4; }
Server Implementation
type userServer struct { apiv1.UnimplementedUserServiceServer store UserStore } func (s *userServer) GetUser(ctx context.Context, req *apiv1.GetUserRequest) (*apiv1.GetUserResponse, error) { user, err := s.store.FindByID(ctx, req.Id) if err != nil { if errors.Is(err, ErrNotFound) { return nil, status.Error(codes.NotFound, "user not found") } return nil, status.Error(codes.Internal, "internal error") } return &apiv1.GetUserResponse{ User: toProtoUser(user), }, nil } func (s *userServer) ListUsers(req *apiv1.ListUsersRequest, stream apiv1.UserService_ListUsersServer) error { users, err := s.store.List(stream.Context(), req.PageSize) if err != nil { return status.Error(codes.Internal, "failed to list users") } for _, user := range users { if err := stream.Send(toProtoUser(user)); err != nil { return err } } return nil }
Interceptors
func LoggingInterceptor(logger *slog.Logger) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { start := time.Now() resp, err := handler(ctx, req) logger.Info("grpc call", "method", info.FullMethod, "duration", time.Since(start), "error", err, ) return resp, err } } func RecoveryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { defer func() { if r := recover(); r != nil { err = status.Errorf(codes.Internal, "panic: %v", r) } }() return handler(ctx, req) } }
Client with Retry
func NewClient(addr string) (apiv1.UserServiceClient, error) { conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor( grpc_retry.UnaryClientInterceptor( grpc_retry.WithMax(3), grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100*time.Millisecond)), grpc_retry.WithCodes(codes.Unavailable, codes.ResourceExhausted), ), ), ) if err != nil { return nil, err } return apiv1.NewUserServiceClient(conn), nil }
gRPC Status Codes
| Code | Use Case |
|---|---|
| OK | Success |
| INVALID_ARGUMENT | Bad input |
| NOT_FOUND | Resource missing |
| ALREADY_EXISTS | Duplicate |
| DEADLINE_EXCEEDED | Timeout |
| UNAVAILABLE | Service down |
| INTERNAL | Server bug |
Troubleshooting
Debug Commands
grpcurl -plaintext localhost:50051 list grpcurl -plaintext -d '{"id": 1}' localhost:50051 api.v1.UserService/GetUser
Usage
Skill("go-grpc")