Las APIs REST son muy populares y conocidas, pero aunque funcionan, tienen varios problemas con los que todos, los que hemos trabajado en una, nos hemos encontrado más de una vez.

Para empezar este tipo de APIs están basados en comunicación síncrona exclusivamente en HTTP y hay múltiples estándares a la hora de definir las URLs, parámetros de entrada, salida, etc. Esto, si bien puede parecer bueno, acaba convirtiéndose en un problema.

Hay que estar continuamente documentando la API para saber las URLs, métodos y parámetros de los que disponemos. Además, es muy común que esta documentación no se lleve a raja tabla y haya parámetros que no estén documentados e incluso que esta documentación tenga poco o nada que ver con la realidad.

Esto es una de las cosas que solucionamos con gRPC y en la que me voy a centrar en este artículo.

¿Cómo es una aplicación con gRPC?

En gRPC la aplicación cliente puede llamar directamente a métodos del servidor como si de objetos locales se tratarán. No tienes que preocuparte por las comunicaciones entre cliente-servidor. Simplemente debes de conocer los servicios, métodos y parámetros de entrada y salida de cada uno de estos.

Hay que tener en cuenta que cuando hablamos de gRPC estamos hablando de un protocolo open source creado por Google basado en comunicación RPC (Remote procedure call). Es el más popular, pero no el único.

Para definir todos esos servicios, métodos y parámetros, en gRPC, se usan los archivos .proto, de Protocol Buffers. Es algo así como la documentación o contratos típicos que hacemos cuando trabajamos en una API REST, pero en este caso, no te la puedes saltar nunca y esto es bueno.

Por ejemplo, partamos con este archivo .proto como definición de nuestro servicio:

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Cómo ves, en este caso, tenemos un servicio llamado Greeter con un único método, SayHello. Este método recibe un objeto HelloRequest y responde con un HelloReply en caso de que no haya error. Ambos objetos también están definidos en este archivo por tanto todo el mundo sabe qué propiedades tienen.

Usando los generadores que nos da el propio Protocol Buffers podremos generar nuestras clases e interfaces. En este caso sería algo similar a esto, aunque puede variar dependiendo del lenguaje escogido.

.
└── HelloWorld/
    ├── GreeterInterface.php
    ├── HelloRequest.php
    └── HelloReply.php

Como puedes ver tendremos una interfaz con el servicio y los objetos de entrada y respuesta. Nuestro trabajo, en el servidor, consistirá únicamente en implementar dicha interfaz para que cada uno de los métodos definidos en él haga lo que se espera.

De esta forma se evitan diferencias en los parámetros de entrada, salida e incluso en métodos o URLs. No tienes que preocuparte por nada de eso, tu contrato es el archivo .proto y, al no poderte salir de lo que se define en él, estos problemas de mala comunicación o cambios en la implementación desaparecen.

Por otro lado, en el cliente, usando este mismo archivo .proto podremos saber exactamente cómo comunicarnos con el servidor y podremos ejecutarlo como si de código local se tratara.

Por ejemplo, en Java, nuestro cliente sería algo así:

ManagedChannel channel = ManagedChannelBuilder
		.forTarget("localhost:50051")
		.usePlaintext()
		.build();

GreeterGrpc.GreeterBlockingStub blockingStub;
blockingStub = GreeterGrpc.newBlockingStub(channel);

HelloRequest request = HelloRequest.newBuilder().setName(name).build();

HelloReply response;
response = blockingStub.sayHello(request);

logger.info("Greeting: " + response.getMessage());

Cómo vez es muy sencillo y gracias al código generado a partir del archivo .proto sabes en todo momento que métodos tiene, qué objetos debes mandar, y qué objeto vas a recibir.

Lo único que necesitamos es una URL en la que está el servidor. El puerto habitual es el 50051.

Por si te lo estas preguntando. Una práctica habitual es crear un repositorio con todos los archivos .proto y, tanto los clientes como el servidor, importa ese repositorio como un submódulo y así, tiene los últimos cambios siempre.

Esto es simplemente una de las ventajas de gRPC frente a una API REST tradicional, hay más, pero las dejamos para otro artículo.