HTTP y sus formas de comunicarse
Tags: network, webdev, programming, tutorial
Published: 2023-05-26 18:13:36

Esta entrega va a estar dedicada a tecnologías y no implementaciones, pero aclaro que todos los métodos mencionados son capaces de ser usados con la gran mayoría de los lenguajes.

La idea principal es presentar todos las formas de comunicación sobre HTTP usadas hasta ahora y compararlas en sus aristas mas importantes.

## Qué son y para qué los necesitamos?

Primero, tenemos que explicar brevemente que es HTTP(S). Por su siglas, es _Hyper Text Transfer Protocol_, la S es de _Secure_ cuando los datos viajan cifrados.

Las paginas de internet se basan en este protocolo para mostrar los archivos que el navegador web interprete, por ejemplo, HTML, CSS, JavaScript, imágenes, PDF y un largo etcétera. Es decir, alguien pregunta usando una URL y un puerto (80 para http y 443/8443 para https) y un server nos devuelve los archivos que el cliente sabe interpretar.

Hasta acá, bastante fácil, de hecho es una arquitectura cliente-servidor estándar. Pero sabemos muy bien (y sino, lo aprendemos ahora) que los sistemas ya un poco mas grandes que una página estática, tenemos que armarnos de varias instancias, servicios web, comunicaciones asíncronas, real-time (near real time) y quien sabe cuantas cosas mas, que a veces no necesitamos, pero las usamos igual.

Ejemplo que si usamos todos, un chat, no podemos hacer la arquitectura _normal_ que vimos en http, porque sería muy lento, además, no podríamos usar otras _features_ como por ejemplo,si nuestro amigo está escribiendo, no seriamos capaces de ver que lo está haciendo si usamos la arquitectura descripta anteriormente.

Es por eso, que vamos a necesitar de mas y naturalmente diversas tecnologías para responder todas las demandas.

Vamos a ver:

1. [gRPC](https://grpc.io) (desarrollado por Google)
2. [REST](https://restfulapi.net/) (representar recursos y estados [JSON])
3. [Webosckets](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwj22vSCnpL_AhUjrZUCHTkWCcwQFnoECAkQAQ&url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FWebSockets_API&usg=AOvVaw2CMyKiqhXN3YmpHc4yjRv_) (canal de ida y vuelta)
4. [GraphQL](https://graphql.org/) (alternativa interesante para escalar)


## gRPC 🎯 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dqv44p4wd1fxyndn0vgm.png)

Desarrollado por Google, pero open source, es un framework tipo plus and play, multiplataforma para comunicación de servicios. Es independiente al lenguaje base de programación y ofrece una serie de ventajas claras en casos de uso específicos, donde es casi siempre la mejor opción y no tiene rival. Las que mas se destacan son su eficiencia, la posibilidad de documentar con código (real) y versionar los modelos, uso de http v2, plugins de autenticación, métricas, checks de salud de las comunicaciones, load balancing y tracing.

#### que necesito y como funciona?

Dos cosas son claves, los protocol buffers (protobuf) que son el mecanismo de serialización - lo podemos comparar con los JSON de REST- y es 100% neutral a las plataformas, sistemas operativos y lenguajes y [el compilador protobuf](https://grpc.io/docs/protoc-installation/).

Así se define uno simple, la definición de un "objeto" Persona
```proto
message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;
}
```
No solo definir estructuras, también interfaces con métodos, o sea el comportamiento que deseamos para nuestro servicio.

```proto
// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
```

Esta definición va a ir en un archivo `.proto`. 
Un buen lugar donde ver todo sobre protocol buffers es [su sitio oficial](https://protobuf.dev/programming-guides/proto3/) y usar la version 3 (al menos a la fecha de publicación de este humilde artículo).

Ahora bien, dijimos que necesitábamos el compilador protobuf y el plugin gRPC para poder generar el código que soporte nuestros archivos proto (al menos en Go).

Para ejecutar el compilador, vamos a correr algo asi:
```bash
protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    proto/{nuestro_archivo}.proto
```

El resultado van a ser archivos `.go` que tienen toda la operatoria definida previamente.

#### Ventajas 👍 
- La principal, su rendimiento. El (des)serializado (cuando transformamos nuestro objeto Java por ej. para que viaje por la red, eso es serializar y la inversa es para leerlo cuando llega y convertimos los bytes a un objeto).
- Usar http2 y su tecnología superior (multiplexación de canales, tasas de transmisión mas altas, etc)
- Posibilidad de *tunear* los servicios de acuerdo a las necesidades:
  

   - Sin streaming
   - Streaming unario del servidor al cliente
   - Streaming unario del cliente al servidor
   - Streaming bidireccional 

Esto corresponde al ciclo de vida de una request/response en http2 y gRPC, sugiero leer [esta documentación](https://grpc.io/docs/what-is-grpc/core-concepts/#unary-rpc) para entenderlo mejor.
- Última ventaja, es poder ser API-first y documentar los contratos, versiones y demás con el tooling de gRPC. Ademas, poder usar versiones viejas de las APIs sin mucho esfuerzo.

#### Desventajas 👎 
- Difícil de probar, básicamente traban en par cliente-servidor, por lo que ir a Postman o cURL no es una opción muy viable.
- El código generado no es legible o entendible por nosotros.
- Se requiere una nueva tecnología y dominarla puede llevar tiempo y errores.

## REST 🎯

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gv3hfmmzknpe7d4ad0ih.png)

Es largamente usado en la industria y seguramente lo siga siendo. Es la primera opción a considerar a la hora de comunicar cliente y servidor.
En esta entrega, vamos a basar a REST con sus mensajes en JSON.

#### qué es y como funciona?

Ahora bien, que es REST, por sus siglas, **RE**presentational **S**tate **T**ransfer. Quiere decir que vamos a presentar recursos y manipularlos a través de estados. Vamos a crear una interfaz uniforme, que sea fácil de entender y extender conforme nuestro servicio crezca en funcionalidades.

Esta interfaz se basa en reglas generales que no cambian en el servicio, esto hace que su comportamiento sea determinista tanto para los usuarios como para los desarrolladores. Algunos lineamientos aceptados son: 
(cabe destacar que estos, como todo, tienen opiniones a favor y en contra. La clave es elegir uno y no cambiarlo).
- Nombres de los recursos en plural para las URLs. Por ejemplo /users.
- Todos los recursos deben ser únicos en su nomenclatura.
- Usar los verbos HTTP para determinadas acciones. `POST` para crear, `GET` para leer, `PUT` para actualizaciones completas y `PATCH` para parciales. `DELETE`, claramente, para borrar.

En rest, decimos que un recurso es un conjunto de datos a los cuales le puedo aplicar algunos de los comportamientos mencionados anteriormente, usando URLs y los verbos. No es lo mismo un GET a `/users` que un POST.

Una feature que comparte REST y HTTP es que las respuestas se pueden **cachear**. Interesante punto para la performance.

Otros temas importantes son la idempotencia, el no-estado (conocido por stateless en inglés) y el tipo de contenido (header de content-type). Obvio que la seguridad, tokens y demás son otros puntos mas que interesantes, pero al haber tantas alternativas, es preferible dejarlo para otra entrega.

**Idempotencia** es una característica propia de los VERBOS, no de los recursos o de las URLs. Quiere decir que una acción (URL + verbo) puede repetirse N veces sin que tenga efectos en los recursos y obtener el mismo resultado una y otra vez. Por ejemplo si ejecuto `DELETE /users/1`, la primera vez, borro sin problemas, la segunda, no voy a borrar nada pero tampoco debería tener un error de vuelta, ya que ese ID ya fue borrado. Por eso decimos que **DELETE** si es idempotente.
<u>
Esto tenemos que tenerlo claro a la hora de programar.</u>

En cambio, POST, no lo es, por que si enviamos 2 veces el mismo usuario, es natural que si obtengamos un error, porque no podemos tener 2 usuarios distintos con el mismo mail. Por eso POST no lo es.

**STATELESS** es una de las propiedades principales de REST, quiere decir que cada petición es totalmente autocontenida y no necesita información de su antecesora. Aunque no es 100% verdad (al menos para mi) porque en APIs que requieran estar autenticado, si tenemos que hacer una antes pera validar credenciales. Pero bueno podemos discutirlo en otro momento.

**JSON** JavaScript object notation, tal como en gRPC la comunicación era por binarios que eran el resultado de los archivos `proto` en REST vemos que JSON es el _de facto_ para la industria. Los cuerpos de las peticiones tienen que tener un JSON válido para que pueda ser leído. Esto da mucha flexibilidad porque cualquier API puede leer mensajes de básicamente, cualquier cliente, siempre y cuando respete el contrato JSON acordado sumado a las mencionadas URL y verbo HTTP. Con este "triplete" tenemos ya toda la información necesaria para si sellar nuestro contrato y poder documentarlo. Para el estándar, esta **OpenAPI** para seguir como  ejemplo.

Por último en REST, otras cosas a tener en cuenta, repasar y documentar.

- Filtros
- Paginación
- Los recursos debería tener los links asociados para operar en él


#### Ventajas 👍
 
- Cacheable
- Fácil de implementar y nativo de todos los lenguajes, ya que se basa en HTTP, entonces, no necesitamos nada mas.
- Ningún cliente de las APIs se va a quejar por usar REST con JSON.
- Para probar, tampoco necesitamos nada, un Postman, cURL, un browser, cualquiera es válido.
- Por lo general, front y back separados.
- Es un FIT muy bueno para la gran mayoría de las apps data driven.

#### Desventajas 👎 

- Es difícil aplicarlo de forma "pura" en todo el proyecto. Si el servicio escala conforme pasa el tiempo, muchas veces tenemos casos de uso que no cumple con las especificaciones de REST, entonces tenemos que ir adoptando excepciones y licencias para cosas nuevas y puede terminar mal.
- La documentación suele ser un dolor de cabeza importante.
- Su performance no es óptima.


## Websockets 🎯

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ctwk55idsau0af1injtm.png)


Es una tecnología que permite la comunicación ida y vuelta en una sesión interactiva entre el cliente y el servidor.

Se basa en una serie de **mensajes** que esta API entiende, por ejemplo si enviamos solo texto (lo único que podemos enviar y que leer), un mensaje de cierre de conexión, apertura, ping para mantener la sesión abierta y alguno mas seguro debe haber, pero no los usé, ya sabiendo esos van a estar mas que bien.

Para usar en cualquier lenguaje de backend, vamos a necesitar alguna **librería**, ya que una implementación propia puede resultar costosa en términos de tiempo y traer algunos bugs difíciles de corregir, por eso recomiendo buscar alguna ya probada ahí afuera.

Tiene muchos casos de uso, al ser tan rápido (solo enviá pocos tipos de eventos) y bidireccional, muchos chats están implementados con **websockets**. Tenemos otros para enumerar.

- Suscripción a tickets de la bolsa/criptomonedas (recibir el precio actualizado en forma de "feed").
- Juegos multi jugador donde necesitamos muchos datos al mismo tiempo y con baja latencia.
- Transmitir coordenadas en near-real-time.
- Herramientas colaborativas (estilo google docs)
- etc

Hay que tener en cuenta que algunos navegadores no soportan websockets, ni todas las implementaciones son iguales. Acá hay [una buena docu de referencia](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API).

#### Ventajas 👍

- Velocidad de transmisión.
- Facilidad para implementarlo.
- Amplio soporte y herramientas.

#### Desventajas 👎

- A veces no es tan trivial el debug, aunque Postman ya incluye request WS.
- Los "internals" son complejos, ademas de que dependemos de librerías en el 99% de los casos.
- No son persistentes, tampoco hay servicios cloud que provean algo así. Cierro el navegador y se cierra el websocket.


## GraphQL 🎯


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhgyeu9s3xeasl9m1fhu.png)



#### Que es?

Muy complicado responder, es varias cosas! Un framework para manipulación de datos, un motor de queries hasta incluso un lenguaje (exagerando un poco). Sabemos también que es opensource y desarrollado (al principio) por [Meta](https://about.meta.com/). Para mi, lo podemos definir como una especificación para APIs.

Es una alternativa a REST, no veo como una evolución natural donde lo que tenemos en REST lo podemos llevar a GQL, ni al revés, hay que pensarlo así desde el principio o en el peor de los casos, reescribir los servicios.

La clave es que el cliente, con la query correcta, puede obtener los datos que necesita y nada mas, es decir, si tenemos un usuario en nuestro modelo que tiene 15 campos, podemos traer cualquier combinación de ellos, al contrario de REST, que el recurso viaja como una unidad, ya que desarrollar algo similar llevaría muchísimo trabajo.

Gran ventaja es la manipulación de datos tan granular que ofrece y con un esfuerzo dentro de todo bajo si ya venciste la curva de aprendizaje.

Otra ventaja es que es independiente a la fuente de datos, puede ser MySQL, mongo, etc, aunque esas conexiones queda para que nosotros las hagamos, no es plug-and-play ni mucho menos.

![GQL engine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/302fllehxbwckkg4v4ci.png)


#### Pasos a seguir

Primero tenemos que definir un esquema (schema) donde describimos nuestro modelo. Al ser tipado, tenemos que declarar todos los tipos, nos queda de este estilo:
```
type User {
  id: ID
  name: String
}

type Project {
  name: String
  tagline: String
  contributors: [User]
}
```

Al definir las queries:
```
type Query {
  getProjects: [Project]
  getUsers: [User]
}
```

Y desde el cliente deberían enviar algo así:
```
{
 project {
   name
   tagline
 }
}
```

Y el resultado esperado en JSON:
```json
{
 "data": {
   "project": [
       {
         "name": "Apollo 11"
       },
       {
         "name": "Mars-Rover"
       },
       {
         "name": "Challenger 1"
       }
     ]
 }
}
```
Todo el framework viene con un tooling importante. No en todos los lenguajes es igual, algunos tienen generadores de código o corren una UI para realizar las consultas GQL de forma muy sencilla.
El detrás de escena es el mismo, el motor funciona igual, las conexiones con las fuentes de datos también.

#### Ventajas 👍

- Reducir drásticamente el numero de URLs, vamos a tener muchas menos (menos lineas == menos para mantener).
- Escalabilidad garantizada.
- Como los `.proto`, la documentación es mucho mas natural y versionada.
- Reutilización de tipos de datos en los schemas.


#### Desventajas 👎

- Curva de aprendizaje
- No podemos hacer cache de respuestas, en REST si.
- Mantener el schema de GQL puede no ser trivial para desarrolladores sin experiencia con el framework.


Bueno, hasta acá mi análisis, espero que les haya gustado, los espero en [YouTube](https://youtube.com/tomaslingotti), [Twitch](https://twitch.tv/tomasitofl) y [Discord](https://discord.io/gophers-latam)! 

PS: Comenten algún tema en particular si quieren que investigue y escriba!