Agent Skills: Go gRPC Skill

Build gRPC services with Go - protobuf, streaming, interceptors

gogRPCprotobufstreaminginterceptors
backendID: pluginagentmarketplace/custom-plugin-go/go-grpc

Skill Files

Browse the full folder contents for go-grpc.

Download Skill

Loading file tree…

skills/go-grpc/SKILL.md

Skill Metadata

Name
go-grpc
Description
Build gRPC services with Go - protobuf, streaming, interceptors

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")