gRPC en Go: microservicios tipados con protobuf en 2026

REST con JSON funciona. Es flexible, lo entiende cualquier herramienta y puedes probarlo con un simple curl. Pero tiene un problema que se nota cuando hay varios microservicios hablando entre sí: no hay contrato de tipos. Si el campo userId pasa a llamarse user_id en el servidor, el cliente falla en tiempo de ejecución, no de compilación.

gRPC resuelve eso con Protobuf. El contrato del servicio vive en un fichero .proto, y tanto el servidor como el cliente se generan automáticamente a partir de él. Si el contrato cambia de forma incompatible, lo sabes antes de desplegar. Además la serialización es binaria, ocupa menos que JSON y va más rápido en benchmarks con mensajes grandes.

La pregunta real es cuándo usar cada uno. Para comunicación interna entre microservicios donde el rendimiento y los tipos importan, gRPC tiene sentido. Para APIs públicas, backends que consumen clientes web o móvil, o cuando necesitas que un humano lea las peticiones en el log, REST sigue siendo la opción razonable. No son excluyentes: muchos proyectos usan ambos según el contexto.

Protobuf: el contrato del servicio

Todo empieza en el fichero .proto. Ahí defines los mensajes y los métodos del servicio. Un ejemplo para un servicio de usuarios:

syntax = "proto3";

package user.v1;

option go_package = "gen/user/v1;userv1";

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListRequest) returns (stream User);
}

message GetUserRequest {
  int32 id = 1;
}

message ListRequest {}

message User {
  int32  id    = 1;
  string name  = 2;
  string email = 3;
}

Con ese fichero y los plugins de generación instalados, ejecutas protoc (o buf generate, que es más cómodo) y obtienes el código Go: la interfaz del servidor, el cliente y los structs de los mensajes. No escribes esa capa a mano.

Para instalar los plugins necesarios:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Implementar el servidor en Go

El código generado incluye una interfaz que tienes que implementar. La forma habitual es embeber UnimplementedUserServiceServer para que si añades métodos nuevos al proto sin implementarlos todavía, el compilador no se queje:

package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "example.com/gen/user/v1"
)

type userServer struct {
    pb.UnimplementedUserServiceServer
}

func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    // En producción aquí irías a la base de datos
    return &pb.User{
        Id:    req.Id,
        Name:  "Ada Lovelace",
        Email: "[email protected]",
    }, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("error al escuchar: %v", err)
    }

    s := grpc.NewServer()
    pb.RegisterUserServiceServer(s, &userServer{})

    log.Println("Servidor gRPC en :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("error al arrancar: %v", err)
    }
}

Ojo con los interceptores: son el equivalente gRPC al middleware de HTTP. Los usas para logging, autenticación, recuperación de panics y trazabilidad. grpc.ChainUnaryInterceptor te permite encadenarlos sin lío.

Implementar el cliente en Go

El cliente es igual de directo. Abres una conexión, creas el cliente generado y llamas al método como si fuera una función local:

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    pb "example.com/gen/user/v1"
)

func main() {
    conn, err := grpc.NewClient(
        "localhost:50051",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        log.Fatalf("error al conectar: %v", err)
    }
    defer conn.Close()

    client := pb.NewUserServiceClient(conn)

    user, err := client.GetUser(context.Background(), &pb.GetUserRequest{Id: 42})
    if err != nil {
        log.Fatalf("error al obtener usuario: %v", err)
    }

    fmt.Printf("Usuario: %s (%s)n", user.Name, user.Email)
}

En producción sustituyes insecure.NewCredentials() por credenciales TLS reales. Y en lugar de conectar a un solo nodo, usas un resolver con balanceo de carga, que gRPC-Go soporta de forma nativa.

Streaming: donde gRPC adelanta a REST

REST no tiene streaming real. WebSockets cubren parte del hueco, pero son otra capa que gestionar. gRPC lo tiene integrado en el protocolo desde el primer día, porque usa HTTP/2 con multiplexación de streams.

Hay tres modalidades además del RPC unario de siempre:

  • Server streaming: el cliente manda una petición y el servidor responde con varios mensajes. Útil para paginar resultados grandes sin cargarlos todos en memoria o para feeds que van llegando.
  • Client streaming: el cliente manda varios mensajes y el servidor responde al final. Típico para subir ficheros grandes o acumular métricas antes de procesarlas.
  • Bidireccional: ambos lados mandan mensajes de forma independiente sobre el mismo stream. El caso de uso más claro es un chat en tiempo real o un canal de telemetría continua.

El método con server streaming se define así en el proto: rpc ListUsers (ListRequest) returns (stream User);. En la implementación del servidor usas stream.Send(&pb.User{...}) en un bucle. En el cliente recibes con stream.Recv() hasta que obtienes io.EOF.

gRPC-Gateway: REST y gRPC desde el mismo proto

Si necesitas exponer tu servicio gRPC como una API REST también (por ejemplo para que la consuma un cliente web o un partner externo), gRPC-Gateway genera un proxy HTTP/JSON a gRPC automáticamente a partir del mismo fichero .proto.

Añades una anotación en el proto:

import "google/api/annotations.proto";

service UserService {
  rpc GetUser (GetUserRequest) returns (User) {
    option (google.api.http) = {
      get: "/v1/users/{id}"
    };
  }
}

Y el gateway traduce las peticiones GET /v1/users/42 a la llamada gRPC correspondiente. Con una sola definición del servicio tienes ambos protocolos sin duplicar lógica.

Connect: el protocolo alternativo que merece atención

Connect (connectrpc.com) es un protocolo creado por Buf, la empresa detrás de buf.build. Es compatible con gRPC pero funciona también sobre HTTP/1.1 y acepta JSON, lo que significa que puedes llamarlo directamente desde el navegador sin proxy y sin gRPC-Gateway.

En Go se usa con github.com/bufbuild/connect-go. El mismo fichero .proto, la misma generación de código, pero el handler resultante es un http.Handler estándar que puedes montar en cualquier servidor HTTP de Go.

Para servicios nuevos donde el navegador va a ser uno de los clientes, vale la pena considerarlo antes de montar la infraestructura extra de gRPC-Gateway.

Tooling en 2026

La cadena de herramientas ha madurado bastante. Estas son las que de verdad usas en el día a día:

  • buf: sustituye a protoc con una experiencia mucho más cómoda. Gestiona dependencias de protos, hace lint de los ficheros .proto y detecta cambios incompatibles con buf breaking. Si empiezas un proyecto hoy, úsalo desde el principio.
  • buf generate: genera el código Go (y de otros lenguajes) sin tener que gestionar los plugins de protoc manualmente. Configuras los plugins en buf.gen.yaml y listo.
  • grpcurl: el curl para gRPC. Prueba métodos desde la terminal, lista los servicios disponibles, manda mensajes JSON. Imprescindible para depurar sin levantar un cliente.
  • evans: cliente gRPC interactivo con REPL. Si grpcurl es el curl, evans es el Postman: tiene autocompletado, historial y permite explorar el servicio de forma interactiva.

Para la observabilidad, google.golang.org/grpc/stats te da hooks para métricas y trazas. OpenTelemetry tiene interceptores gRPC ya listos para conectar con Jaeger, Tempo o lo que uses.

Autenticación y seguridad

gRPC no incluye autenticación por defecto, pero hay dos patrones bien establecidos. El primero es mTLS: tanto el servidor como el cliente presentan certificado, y la conexión queda autenticada a nivel de transporte. El segundo es JWT o tokens en los metadatos: el cliente manda el token en el header Authorization y un interceptor en el servidor lo valida antes de que llegue al método.

Para servicios internos en Kubernetes, mTLS con Istio o Linkerd lo gestiona el service mesh sin tocar el código de la aplicación. Para validación de JWT propia, los interceptores son la forma limpia de hacerlo sin repetir lógica en cada método.

Más información en la documentación oficial de gRPC para Go (grpc.io/docs/languages/go) y en la referencia de Protobuf (protobuf.dev).

Si estás construyendo microservicios en Go, también te interesa leer sobre Go en microservicios en 2025 y la comparativa de Go vs Rust para comunicación entre servicios.

Imagen: Pexels / Pixabay

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP