Implementando servidor gRPC con PostgreSQL en Go

Resumen

Crear un servidor gRPC en Go implica algo más que definir el archivo proto: necesitas conectar la base de datos, el repositorio y el servidor en una sola pieza funcional. Aquí te muestro cómo armar un servicio nuevo de test en Go usando PostgreSQL, manteniendo la misma arquitectura del StudentServer previo y dejándolo listo para probar en Postman.

Cómo defines la tabla y el modelo para un nuevo servicio gRPC

Todo arranca en la base de datos. Antes de tocar Go, abres el archivo up.sql y declaras la tabla nueva con las columnas mínimas que vas a manejar.

  • Crea la tabla tests con un id de tipo varchar como llave primaria.
  • Agrega un campo name que no acepte valores nulos.
  • Antepón un DROP TABLE IF EXISTS para evitar conflictos si la tabla ya existe [02:15].

Luego vas a la carpeta de modelos y creas el struct Test con dos campos: Id y Name, ambos de tipo string. Este modelo es el que viajará entre el repositorio y el servidor, y no debe confundirse con el que genera el compilador de protobuf.

¿Por qué se usan dos structs distintos para Test? Porque uno representa la entidad en tu base de datos (modelo interno) y el otro es el mensaje serializado que viaja por gRPC. Mantenerlos separados protege tu dominio de cambios en el contrato.

Cómo extiendes el repositorio para soportar getTest y setTest

El patrón repository te permite abstraer el acceso a datos. En la interfaz del repositorio agregas dos métodos nuevos junto a los que ya existían para student.

  • GetTest(ctx, id string) (*models.Test, error) para leer un registro por ID.
  • SetTest(ctx, test *models.Test) error para insertar un nuevo test.
  • Replica esas firmas en la implementación abstracta que delega a la conexión activa [04:30].

Después vas al archivo postgres.go y duplicas la lógica de setStudent y getStudent. Cambias el nombre de la tabla a tests, eliminas la columna de edad porque ya no aplica, y ajustas el insert para que solo reciba id y name. En el getTest mapeas el resultado de la query al struct Test y devuelves el puntero junto con el error.

Qué debes revisar al portar la lógica de un servicio a otro

La tentación de copiar y pegar es real, pero hay tres puntos donde suelen aparecer errores silenciosos.

  • Verifica que el nombre de la tabla coincida exactamente con el SQL: aquí es tests, con s al final.
  • Elimina cualquier campo heredado del modelo anterior, como la edad del estudiante.
  • Renombra las variables locales para que student se convierta en test y evites confusiones al leer el código.

Cómo construyes el TestServer y lo conectas a gRPC

Dentro de la carpeta server creas un archivo nuevo llamado test.go. Ahí defines el struct TestServer que recibe el repositorio y embebe UnimplementedTestServiceServer mediante composición, no herencia.

El constructor NewTestServer(repo) devuelve una instancia lista para registrarse en gRPC. A partir de ahí implementas los dos métodos unarios:

  • GetTest recibe un context.Context y un *testpb.GetTestRequest, llama a repo.GetTest(ctx, req.GetId()) y devuelve un *testpb.Test con Id y Name.
  • SetTest recibe un *testpb.Test, lo convierte al modelo interno, lo persiste con repo.SetTest(ctx, test) y responde con un *testpb.SetTestResponse que incluye el ID y el nombre insertados [09:45].

¿Cuál es la diferencia entre el Test del proto y el Test del modelo? El primero lo genera el compilador de protobuf y se usa solo para comunicación gRPC. El segundo lo defines tú en models y es el que entiende el repositorio. Convertir entre ambos es responsabilidad del servidor.

Cómo levantas el servidor en un puerto distinto

Para no chocar con el StudentServer, creas la carpeta server-test con su propio main.go. Copias la estructura del servidor previo y aplicas tres cambios:

  • Cambia el puerto de escucha para evitar colisión con el servicio anterior.
  • Reemplaza NewStudentServer por NewTestServer pasándole el mismo repositorio.
  • Registra el servicio con testpb.RegisterTestServiceServer y activa la reflection para que Postman descubra los métodos automáticamente.

La URL de la base de datos se mantiene igual porque ambos servicios comparten la instancia de Postgres.

Cómo pruebas los métodos unarios desde Postman

Levantas el contenedor de Postgres con docker run mapeando el puerto 54321:5432 y ejecutas el servidor con go run server-test/main.go. En Postman creas un gRPC request apuntando a localhost:5070 y activas la reflexión.

  • Invocas SetTest con id: "T1" y name: "Golang 101". La respuesta confirma la inserción.
  • Copias ese ID y lo usas en GetTest. El servidor devuelve el mismo id y name que acabas de guardar.
  • Si algo falla, revisa que el repositorio reciba el modelo correcto y no el mensaje protobuf [16:20].

¿Qué es un método unario en gRPC? Es una llamada con un único request y un único response, igual que una petición REST tradicional. Es el patrón más simple antes de pasar a streaming.

Con esto ya tienes dos servicios gRPC funcionando en paralelo sobre la misma base de datos. El siguiente paso natural es romper el patrón unario y dejar que el cliente envíe un flujo continuo de datos al servidor. ¿Te animas a contarme en los comentarios qué servicio implementarías tú primero con streaming?