Streaming del servidor con gRPC en Go

Resumen

El streaming del lado del servidor en gRPC permite que el servidor responda al cliente con múltiples mensajes a través de una sola conexión, ideal cuando necesitas enviar listas extensas o datos que se generan en el tiempo. Aquí verás cómo implementarlo en Go usando dos métodos prácticos: enrolar estudiantes y obtener estudiantes por test.

¿Cómo se define un método de streaming en el servidor gRPC?

Todo arranca en el archivo del servidor donde ya tenías registrados métodos como SetTest y SetQuestions. Ahora vas a sumar dos receiver functions nuevas dentro de server/test.go [01:00].

La primera, EnrollStudents, recibe un stream en lugar de un request normal. Esto tiene sentido porque a nivel de proto buffer declaraste que el método consume un flujo continuo de datos desde el cliente. La firma queda así: recibe un stream testpb.TestService_EnrollStudentsServer y devuelve un error.

¿Qué es un stream en gRPC? Es un canal de comunicación que permite enviar múltiples mensajes entre cliente y servidor sin abrir nuevas conexiones. Puede fluir desde el cliente, desde el servidor o en ambas direcciones.

¿Cómo iterar los mensajes que llegan por el stream?

Dentro de la función necesitas un bucle que llame a stream.Recv(). Esa llamada devuelve dos valores: el mensaje recibido (de tipo EnrollmentRequest) y un error.

La magia de trabajar con streaming es saber cuándo se ha cerrado la conexión. Para eso existe un error especial llamado EOF (end of file), que indica que el cliente terminó de enviar datos. Si detectas ese error, debes responder con stream.SendAndClose() enviando un SetQuestionResponse con Ok: true.

Si el error es distinto de EOF, simplemente lo retornas porque significa una falla de lectura o una conexión cerrada inesperadamente.

¿Cómo procesar los datos recibidos y guardarlos en base de datos?

Por cada mensaje válido del stream construyes un nuevo enrollment del tipo models.Enrollment con el StudentId y el TestId extraídos con msg.GetStudentId() y msg.GetTestId() [03:30].

Luego invocas tu repositorio con repository.SetEnrollment(ctx, enrollment), pasando un contexto en background. Si esa inserción falla, también cierras el stream con SendAndClose pero esta vez con Ok: false.

¿Por qué Go marca el error de variable no declarada?

Un detalle clásico: al crear el enrollment de forma implícita debes usar := y no solo =. Sin los dos puntos, Go asume que ya declaraste la variable antes y lanza el error de "no declarado". Con := Go infiere el tipo y la crea en el momento.

¿Cómo devolver un stream desde el servidor con GetStudentsPerTest?

El segundo método invierte el flujo: el cliente envía un único request y el servidor responde con un stream de estudiantes [05:45].

La firma recibe un request *testpb.GetStudentsPerTestRequest y un stream testpb.TestService_GetStudentsPerTestServer, y devuelve un error.

Los pasos dentro de la función son:

  • Llamar al repositorio con GetStudentsPerTest(ctx, req.GetTestId()) para obtener la lista.
  • Verificar el error y retornarlo si existe.
  • Iterar la lista y construir un objeto studentpb.Student con Id, Name y Age.
  • Enviar cada estudiante con stream.Send(student).
  • Agregar un time.Sleep(2 * time.Second) entre envíos para apreciar el efecto de streaming.
  • Retornar nil al terminar.

¿Para qué sirve el time.Sleep en un stream? Solo para fines didácticos. Te permite ver cómo el cliente recibe los mensajes con pausas reales, confirmando que la data llega de forma progresiva y no como un bloque único.

Un detalle importante: aunque estés en el servicio de test, el tipo Student vive en el paquete studentpb, no en testpb. Ajusta el import para evitar errores de compilación.

¿Cómo probar el streaming del servidor con Postman?

Antes de probar necesitas reconstruir la base de datos porque hubo cambios de esquema. Usa docker build . -t platzi-grpc-db dentro del directorio correspondiente y luego docker run -p 54321:5432 platzi-grpc-db para levantarla [10:30].

Después compila los archivos proto tanto de test como de student, y arranca ambos servidores con go run server/student/main.go y go run server/test/main.go en terminales separadas.

La secuencia de pruebas en Postman es la siguiente:

  1. Crear tres estudiantes con SetStudent en localhost:5060 con IDs S1, S2 y S3 y edades 20, 24 y 22.
  2. Crear un test nuevo con ID T1 y nombre Golang 101 en localhost:5070.
  3. Invocar EnrollStudents (streaming del cliente) y enviar los tres pares student/test, cerrando con end streaming.
  4. Invocar GetStudentsPerTest con el ID T1 y observar cómo los estudiantes llegan uno a uno con la pausa de dos segundos.

¿Qué diferencia hay entre streaming del cliente y del servidor?

En el streaming del cliente, como EnrollStudents, es el cliente quien envía mensajes repetidamente y el servidor responde una sola vez al final. En el streaming del servidor, como GetStudentsPerTest, el cliente manda un solo request y el servidor responde con múltiples mensajes a lo largo del tiempo.

Ambos patrones se combinan en el siguiente paso: el streaming bidireccional, donde cliente y servidor hablan en paralelo por el mismo canal. ¿Ya pensaste qué casos de uso tendría en tu propio backend? Cuéntalo en los comentarios.