Creando un API Microservicios con GraphQL + Apollo Gateway y MySQL
Tags: graphql
Published: 2021-09-11 22:00:50

Hace unos días que rondaba en mi cabeza el cómo poder diseñar una arquitectura basada en servicios utilizando el Stack que actualmente estoy aprendiendo "GraphQL + Apollo + NodeJS + TypeScript y MySQL" 

Evidentemente hasta ahora no había tenido necesidad de hacer algo parecido al menos no en mis anteriores proyectos, sin embargo todo nace por una necesidad "cómo podría optimizar los procesos de entrega en la página de RockandRo".
 
Anteriormente había diseñado un API basada en REST, un monolito, sin embargo las necesidades del juego van cambiando así como las mías como cliente. 

En esta entrada realizaremos un pequeño ejercicio de un API basada en Microservicios utilizando GraphQL.

## Tabla de contenido 
  * [¿Qué es un microservicio?](#chapter-1)
  * [Problemática](#chapter-2)
  * [Codificando servicio proveedor](#chapter-3)
  * [Codificando servicio Gateway](#chapter-4)

### ¿Qué es un microservicio? <a name="chapter-1"></a>

Según la documentación proveída por Microsoft Azure define a los microservicios cómo "un método arquitectónico para crear aplicaciones donde cada función (o servicio) principal se compila e implementa de manera independiente". (Ref)

Esto que significa en palabras "mortales", tu puedes definir tus propias funciones en diferentes lenguajes de programación en diferentes lenguajes de programación e ir agregando funcionalidad, así mismo si uno de los servicios llegase a fallar el resto podría seguir funcionando, gracias a que la arquitectura es distribuida y ligeramente acoplada, cosa que con un sistema basado en una arquitectura en Monolito causaría la detención del mismo. 

### Problemática  <a name="chapter-2"></a>

Nota: Utilizaré la palabra cliente para hacer referencia a la entidad o persona que contrató nuestros servicios.

_El cliente desea crear, obtener, modificar y eliminar la información de sus proveedores_

_¿Qué información desea almacenar el cliente de sus proveedores?_

* _Nombre del proveedor_
* _Representante de ventas_ 
* _Teléfono de contacto_ 
* _Correo Electrónico_ 

### Codificando servicio proveedor <a name="chapter-3"></a>

#### Creando la base de datos

Creamos una nueva base de datos, en mi caso la llamaré proveedores:
```sql
CREATE DATABASE proveedores;
```

Posteriormente creamos la tabla con la información descrita en el apartado Problemática. 

```sql
CREATE TABLE proveedor (nombreProveedor VARCHAR(150),
representante VARCHAR(120),
telefono VARCHAR(10),
email VARCHAR(150),
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY)
```

Ya tenemos lo básico para iniciar. 

### Arquitectura de la aplicación


En primera instancia vamos a crear el servicio _"proveedor" (service-proveedor)_ las dependencias que utilizaré serán las siguientes: 

* _"@apollo/gateway": "^0.40.0",_
* _"@graphql-tools/load-files": "^6.4.0",_
* _"@wiicamp/graphql-merge-resolvers": "^2.1.2",_
* _"apollo-server": "^3.0.2",_
* _"apollo-server-core": "^3.0.2",_
* _"apollo-server-express": "^3.1.2",_
* _"dotenv": "^10.0.0",_
* _"graphql": "^15.5.2",_
* _"graphql-tools": "^8.2.0",_
* _"mysql": "^2.18.1",_
* _"mysql2": "^2.2.5",_
* _"ts-node": "^10.0.0",_
* _"ts-node-dev": "^1.1.1",_
* _"typescript": "^4.3.5"_

_Dependencias de desarrollo_
 
* _"@types/express": "^4.17.12",_
* _"@types/express-graphql": "^0.9.0",_
* _"@types/graphql": "^14.5.0",_
* _"@types/node": "^16.4.0"_

### Requerimientos funciones 
* Crear un nuevo proveedor 
* Editar la información de un proveedor 
* Eliminar un proveedor de la base de datos
* Obtener la información de un proveedor 
* Obtener la lista de proveedores

### Definición del archivo SQL_PROVEEDORES.ts 

_En este archivo definiremos las consultas a la base de datos que prácticamente son las reglas del negocio definidas en el apartado Requerimientos funcionales, este archivo tendrá el siguiente contenido._

```typescript
//Crear un proveedor
export const CREARP_ROVEEDOR =
  "INSERT INTO proveedor (nombreProveedor,representante,telefono,email) VALUES (?,?,?,?)";
//editar proveedor
export const EDITAR_PROVEEDOR =
  "UPDATE SET nombreProveedor=?, representante=?, telefono=?, email=? SET WHERE id=?";
//Eliminar proveedor
export const DELETE_PROVEEDOR = "DELETE FROM proveedor WHERE id= ? ";
//Listar proveedor
export const SELECT_PROVEEDOR = "SELECT  FROM proveedor WHERE id=? LIMIT 1";
//Listar todos los proveedores
export const SELECT_ALL_PROVEEDORES = "SELECT  FROM proveedor ";
``` 
Creado esto, nos dirigimos a la carpeta config  donde crearé los siguientes dos archivos: 
* .env 
* mysql.persistences.ts

En el archivo .env crearemos las variables de entorno
``` 
NODE_ENV="DEVELOPMENT"
PORT="PUERTO_DONDE_ESCUCHARÁ_EL_SERVICIO"
MYSQL_HOST="IP/URL DEL SERVIDOR MYSQL"MYSQL_USER="USUARIO_DB"
MYSQL_PASSWORD="CONTRASEÑA_DB"
MYSQL_DB="nombre_de_la_base_de_datos"
```

Ahora, en el archivo mysql.presistences.ts agregaremos el siguiente código, este código nos permitirá conectarnos con la base de datos

```typescript
import { createPool } from "mysql2/promise";
export default createPool({
  host: process.env.MYSQL_HOST,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
  database: process.env.MYSQL_DB,
  decimalNumbers: true,
});
```

Ya que tenemos estos archivos creados, nos vamos a la carpeta Schema, donde crearemos el _schema_ proveedor, el cual quedará definido de la siguiente manera: 

```graphql
type Query {
  obtenerProveedores: [Proveedor]
  obtenerProveedor(id: ID): Proveedor
}

type Mutation {
  addProveedor(proveedor: ProveedorInput): String
  updateProveedor(proveedor: ProveedorInput): String
  deleteProveedor(id: ID): String
}
type Proveedor {
  id: ID
  nombreProveedor: String
  representante: String
  telefono: String
  email: String
}

input ProveedorInput {
  nombreProveedor: String
  representante: String
  telefono: String
  email: String
  id: ID
}
```
Básicamente ya tenemos la estructura del servicio a continuación trabajaremos en la definición de los _resolvers_, dentro de esta carpeta tenemos _mutation_ y _query_ que básicamente son las operaciones que realizará el servicio, mismas que fueron definidas anteriormente en las reglas del negocio. 

Inicialmente empezaré a trabajar en la carpeta _mutation_ en esta carpeta crearemos dos archivos nuevos: 

* _index.ts_ El cual "unirá" los archivos de mutaciones 
* _proveedores.ts_ El cual contiene la definición de las reglas de negocio de _Agregar, Eliminar, Editar_ 

Dentro del archivo _poveedores.ts_ definimos las siguientes funciones. 

```typescript
import {
  CREAR_PROVEEDOR,
  DELETE_PROVEEDOR,
  EDITAR_PROVEEDOR,
} from "../../sql/SQL_PROVEEDORES";

const resolverMutationProveedor = {
  Mutation: {
    //Agregar un nuevo proveedor a la base de datos
    async addProveedor(_: any, { proveedor }: any, { sql }: any) {
      try {
        let { nombreProveedor, representante, telefono, email } = proveedor;
        await sql.query("START TRANSACTION");
        await sql.query(CREAR_PROVEEDOR, [
          nombreProveedor,
          representante,
          telefono,
          email,
        ]);
        await sql.query("COMMIT");
        return "Operación finalizada con éxito";
      } catch (error: any) {
        await sql.query("ROLLBACK");
        throw new Error(error);
      }
    },
    //Actualizar la información del proveedor
    async updateProveedor(_: any, { proveedor }: any, { sql }: any) {
      try {
        let { nombreProveedor, representante, telefono, email, id } = proveedor;
        await sql.query("START TRANSACTION");
        await sql.query(EDITAR_PROVEEDOR, [
          nombreProveedor,
          representante,
          telefono,
          email,
          id,
        ]);
        await sql.query("COMMIT");
        return "Información actualizada correctamente";
      } catch (error: any) {
        throw new Error(error);
      }
    },
    //Eliminar registro
    async deleteProveedor(_: any, { id }: any, { sql }: any) {
      try {
        await sql.query("START TRANSACTION");
        await sql.query(DELETE_PROVEEDOR, [id]);
        await sql.query("COMMIT");
        return "Registro eliminado";
      } catch (error: any) {
        await sql.query("ROLLBACK");
        throw new Error(error);
      }
    },
  },
};

export default resolverMutationProveedor;

```

Dentro del archivos _index.ts_ definiremos las siguientes funciones. 

```typescript
const GMR = require("@wiicamp/graphql-merge-resolvers");

//Query
import resolverMutationProveedor from "./proveedores";

const resolverMutation = GMR.merge([resolverMutationProveedor]);

export default resolverMutation;

```
A continuación definiremos el contenido de la carpeta _query_ al igual que _mutations_ esta tendrá dos archivos, uno llamado _index.ts_ donde uniremos los archivos de query a medida que nuestro servicio vaya creciendo y otro llamado _proveedores.ts_

Definiendo el archivo _proveedores.ts_ 

```typescript
import {
  SELECT_ALL_PROVEEDORES,
  SELECT_PROVEEDOR,
} from "./../../sql/SQL_PROVEEDORES";
const resolverQueryProveedor = {
  Query: {
    async obtenerProveedores(_: any, __: any, { sql }: any) {
      try {
        const [rows]: [any] = await sql.execute(SELECT_ALL_PROVEEDORES);
        return rows;
      } catch (error: any) {
        throw new Error(error);
      }
    },
    async obtenerProveedor(_: any, { id }: any, { sql }: any) {
      try {
        const [rows]: [any] = await sql.execute(SELECT_PROVEEDOR, [id]);
        return rows[0];
      } catch (error: any) {
        throw new Error(error);
      }
    },
  },
};

export default resolverQueryProveedor;
```

Definiendo nuestro archivo _index.ts_
```typescript
const GMR = require("@wiicamp/graphql-merge-resolvers");

//Query
import resolverQueryProveedor from "./proveedor";

const mainResolver = GMR.merge([resolverQueryProveedor]);

export default mainResolver;

```

Básicamente hemos terminado con nuestro servicio de proveedores, lo único que falta es definir el archivos server.ts, el cual lucirá de la siguiente forma 

_server.ts_

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9owumw7hgychpgsqeuju.png)
 


Al final, la estructura de nuestro servicio lucirá de la siguiente manera. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aaf9iqqbhqwe0y2518ul.png)

### Codificando servicio Gateway <a name="chapter-4"></a>

Este es mas sencillo de codificar. 

Dependencias 

* _"@apollo/gateway": "^0.41.0",_
* _"apollo-server": "^3.3.0",_
* _"dotenv": "^10.0.0",_
* _"ts-node": "^10.0.0",_
* _"graphql": "^15.5.2",_
* _"typescript": "^4.3.5"_

Dependencias de desarollo
* _"@types/graphql": "^14.5.0"_
* _"@types/node": "^16.4.0",_
* _"ts-node-dev": "^1.1.1"_

Creamos el archivo server.ts dentro de la carpeta src, el cual tendrá el siguiente contenido. 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2uiyl88wr2nfwkjajeq.png)
 
Con esto tendremos funcionando un API basada en Microservicios utilizando GraphQL, MySQL y Apollo Gateway.
 
Puedes acceder al repositorio del código fuente desde aquí https://github.com/DiWolf/api-servicios-graphql