DEV Community

Cover image for Creando un API Microservicios con GraphQL + Apollo Gateway y MySQL
Francisco Javier Guerrero Martinez
Francisco Javier Guerrero Martinez

Posted on

Creando un API Microservicios con GraphQL + Apollo Gateway y MySQL

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?

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

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

Creando la base de datos

Creamos una nueva base de datos, en mi caso la llamaré proveedores:

CREATE DATABASE proveedores;
Enter fullscreen mode Exit fullscreen mode

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

CREATE TABLE proveedor (nombreProveedor VARCHAR(150),
representante VARCHAR(120),
telefono VARCHAR(10),
email VARCHAR(150),
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY)
Enter fullscreen mode Exit fullscreen mode

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.

//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 ";
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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

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,
});
Enter fullscreen mode Exit fullscreen mode

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:

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
}
Enter fullscreen mode Exit fullscreen mode

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.

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;

Enter fullscreen mode Exit fullscreen mode

Dentro del archivos index.ts definiremos las siguientes funciones.

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

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

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

export default resolverMutation;

Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

Definiendo nuestro archivo index.ts

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

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

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

export default mainResolver;

Enter fullscreen mode Exit fullscreen mode

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

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

image

Codificando servicio Gateway

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

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

Top comments (0)