Client streaming en gRPC con Go

Resumen

El client streaming en gRPC te permite enviar múltiples mensajes desde el cliente al servidor y recibir una sola respuesta al final. Aprenderás a implementarlo en Go usando archivos .proto, PostgreSQL y Postman, ideal si construyes APIs eficientes y escalables.

Esta funcionalidad es una de las razones por las que gRPC se volvió tan popular frente a REST: en lugar de abrir una conexión por cada request, abres una sola y envías un flujo continuo de datos.

Qué es el client streaming en gRPC y cuándo usarlo

Cuando hablamos de client streaming, nos referimos a un patrón donde el cliente abre una conexión, envía varios mensajes y el servidor responde una única vez al cerrarse el flujo. Es útil cuando no sabes con exactitud cuántos elementos se enviarán, como cargar preguntas de un test, subir métricas o procesar lotes en tiempo real.

¿Qué es client streaming en gRPC? Es un tipo de comunicación donde el cliente envía un flujo de mensajes al servidor, y el servidor devuelve una sola respuesta al finalizar el stream. Se define con la palabra clave stream en el archivo .proto.

Cómo se define el stream en el archivo proto

Todo empieza en el archivo .proto, que actúa como contrato entre cliente y servidor [03:10]. Aquí defines los mensajes y los métodos RPC que se van a exponer.

En el caso de las preguntas de un test, necesitas tres piezas:

  • Un mensaje Question con id, answer, question y test_id.
  • Un mensaje SetQuestionResponse con una bandera booleana ok.
  • Un método RPC SetQuestions que reciba stream Question y retorne SetQuestionResponse.

La palabra clave stream delante del tipo Question es la que le indica a gRPC que el cliente enviará múltiples mensajes en lugar de uno solo. Después debes compilar el .proto con protoc para regenerar los paquetes de Go [04:50].

Cómo modelar la tabla questions en PostgreSQL

En el archivo SQL creas la tabla questions con los siguientes campos:

  • id tipo VARCHAR(32) como llave primaria.
  • test_id tipo VARCHAR(32) NOT NULL.
  • question tipo VARCHAR(255) NOT NULL.
  • answer tipo VARCHAR(255) NOT NULL.
  • Una llave foránea de test_id hacia la tabla tests.

La llave foránea garantiza la integridad referencial: cada pregunta pertenece a un test existente [06:20].

Cómo implementar SetQuestion en el repositorio de Go

En la capa de modelos defines un struct Question con sus tags JSON correspondientes, incluyendo test_id para la serialización [07:40]. Luego, en repository.go, agregas la firma del método:

  • SetQuestion(ctx context.Context, question *models.Question) error en la interfaz.
  • La función abstracta que delega en la implementación concreta.

En postgres.go implementas la query INSERT INTO questions con cuatro valores parametrizados ($1, $2, $3, $4) que corresponden a id, answer, question y test_id [09:15]. Este patrón evita inyección SQL y mantiene el código limpio.

Cómo manejar el stream en el servidor con EndOfFile

Aquí viene la parte interesante. La función SetQuestions en el servidor recibe un parámetro de tipo TestService_SetQuestionServer, que representa el stream abierto con el cliente [10:30].

Dentro de la función necesitas un for infinito porque no sabes cuántos mensajes llegarán. En cada iteración llamas a stream.Recv(), que bloquea la ejecución hasta que el cliente envíe un nuevo mensaje de tipo Question.

¿Qué hace EndOfFile en gRPC streaming? Es el error especial que recibes cuando el cliente cierra el stream. No indica un fallo, sino que ya no hay más mensajes y debes responder con SendAndClose.

El flujo de control se ve así:

  • Si err == io.EOF, devuelves stream.SendAndClose(&SetQuestionResponse{Ok: true}).
  • Si err != nil por otra razón, retornas el error.
  • Si todo va bien, conviertes el mensaje a models.Question con los getters GetId(), GetAnswer(), GetQuestion() y GetTestId(), y llamas al repositorio.

Si falla el insert, también cierras el stream con SendAndClose pero con Ok: false para que el cliente sepa que algo salió mal [13:50].

Por qué usar context.Background al insertar

Al llamar repository.SetQuestion(context.Background(), question), le pasas el contexto donde se ejecutan los procesos del servidor. Este contexto permite controlar timeouts y cancelaciones de la operación de base de datos.

Cómo probar client streaming con Postman

Postman ya soporta gRPC con server reflection activado [15:20]. Cuando seleccionas el método SetQuestions, aparece un icono que indica streaming del lado del cliente.

Los pasos para probarlo son:

  1. Crear primero un test con SetTest (por ejemplo, T1 con título Golang 101).
  2. Invocar SetQuestions para abrir la conexión.
  3. Enviar varias preguntas con el mismo test_id usando el botón Send.
  4. Hacer clic en Done para cerrar el stream.

Al cerrar la conexión, el servidor responde con ok: true y guardas todas las preguntas en la tabla con una sola respuesta del servidor. Así confirmas que enviaste múltiples mensajes y recibiste una única respuesta consolidada [17:00].

¿Has implementado streaming bidireccional o solo del lado del cliente? Cuéntame en los comentarios qué casos de uso se te ocurren para este patrón.