Comprender las mutaciones en GraphQL

Índice
  1. Diseño de un esquema de mutación
  2. Creando una mutación
  3. Nombramiento de mutaciones
  4. Pasar variables de consulta a mutaciones
  5. Construyendo una aplicación
    1. Configuración de un servidor de desarrollo
    2. Creación de otros archivos de proyecto
  6. Creación de la lógica y la API de la aplicación
    1. Iniciando el servidor
  7. Prueba de mutaciones en Graphiql
    1. Creando un nuevo chef
    2. Actualizar un Chef existente
    3. Eliminar un Chef
  8. Conclusión

Esta es la continuación de una serie sobre acciones de GraphQL. En el tutorial anterior, se analizaron las consultas de GraphQL. Esta vez, analizaremos más de cerca las mutaciones. Existen muchas similitudes en la forma en que funcionan las consultas y mutaciones de GraphQL; sin embargo, las mutaciones tienen más que ver con la mutación de datos que con la consulta de los mismos. En este artículo, profundizaremos más en ellas y trataremos sus conceptos en detalle.

La mutación en cualquier aplicación GraphQL es fundamental para la integridad de los datos de la aplicación. Si la mutación se realiza de forma incorrecta, los datos de salida de dicha aplicación serán erróneos y, si dichos datos se utilizan para la toma de decisiones, podría resultar catastrófico.

Por eso, el objeto de solicitud de mutación no es como el objeto de solicitud de consulta, que no necesita prefijar con una palabra clave antes de realizar solicitudes de consulta. El prefijo de mutación antes de la solicitud es para asegurarse de que la solicitud sea intencional y que quien la realiza conozca las consecuencias de su acción.

Diseño de un esquema de mutación

El diseño de un esquema para una mutación es bastante similar al de una consulta. Veamos el siguiente bloque de código que nos muestra cómo escribir un esquema de mutación:

let chefs = []const Mutations = new GraphQLObjectType({  name: 'Mutation',  fields: {    addChef: {      type: ChefType,      kwarg: {        name: { type: GraphQLString },        age: { type: GraphQLInt},        hobby: { type: GraphQLInt},      },      resolve(parent, kwarg) {        kwarg.id = Math.random()        return [...chefs, kwarg]      }    },  }})

El esquema describe una mutación llamada addChefque agrega los datos sobre un chef a una matriz (ya que en realidad no estamos creando una aplicación real). El esquema contiene algunos pares clave/valor o propiedades que permiten que GraphQL sepa que estamos tratando de crear un esquema para una mutación. Una de ellas es la namepropiedad que hemos establecido para la mutación. Otra es la fieldspropiedad que a su vez tiene otras dos subpropiedades, que son addChefy la resolvefunción.

El addChefobjeto contiene información sobre la función de mutación. Puede tener cualquier nombre. Este es el nombre al que se hará referencia cuando intentemos mutar un dato. El kwargobjeto contiene los argumentos que la función addChefesperará y sus tipos de datos GraphQL.

Por último, tenemos la función de resolución, que es donde se llevan a cabo todas las acciones de mutación. En este caso, acepta dos argumentos. Uno es el argumento principal, que apunta a los datos vinculados a la addChefconsulta, que no tenemos en este caso. Por lo tanto, el argumento principal no es muy útil aquí. El segundo argumento es kwarg, que hace referencia a los argumentos que hemos pasado a la función.

En este punto, hemos creado una mutación, pero aún no funciona. Necesitamos encontrar una forma de que GraphQL sepa que lo que hemos hecho aquí es una mutación. En otras palabras, necesitamos encontrar una forma de registrar la nueva instancia que acabamos de crear como una mutación. Para registrar nuestra mutación, la definimos de la siguiente manera:

module.exports = new GraphQLSchema({  mutation: Mutations})

Creando una mutación

Una vez que haya diseñado un esquema de mutación y lo haya registrado en la aplicación, puede utilizar la mutación addChefen este caso para agregar datos (un chef) a la matriz de chefs. Si, por ejemplo, queremos crear un nuevo chef, podemos ejecutar esta mutación:

mutation {  addChef(name: "Swae Yu", age: "30", hobby:"Swimming"){    id,    age,    name,    hobby  }}

Lo que hemos hecho aquí es utilizar la mutación para agregar datos a la chefsmatriz. También notarás que tuvimos que definir la solicitud dentro de un objeto de mutación. Esto es para que GraphQL reconozca que no solo estamos tratando de obtener datos de algún lugar, sino que queremos alterar dichos datos.

Nombramiento de mutaciones

Por mucho que solo podamos usar la mutationpalabra clave para llevar a cabo nuestra acción de mutación, en producción o en aplicaciones más robustas, será mejor nombrar mutaciones individuales para mayor claridad y facilidad de uso. Dicho esto, podríamos decidir ser más descriptivos y darle un nombre único a la mutación que teníamos en el ejemplo anterior, este será el resultado:

    mutation mutateChef {      addChef(name: "Swae Yu", age: "30", hobby:"Swimming"){        id,        age,        name,        hobby      }    }

Ahora podemos referirnos a esta mutación con su nombre único, mutateChef.

Pasar variables de consulta a mutaciones

Consideremos una situación en la que queremos que un usuario actualice los datos de nuestra aplicación introduciendo nuevos datos, tal vez desde un campo de entrada de texto. Este suele ser el caso en la mayoría de las aplicaciones, pero en este momento no es posible en nuestra aplicación, ya que hemos introducido valores estáticos directamente en nuestra consulta. Lo que podemos hacer es utilizar variables para introducir datos dinámicos en la consulta.

Afortunadamente, este es un proceso muy sencillo en GraphQL. Cambiemos la consulta de mutación que escribimos anteriormente para utilizar entradas variables:

    mutation ($name: String!, $age: Int!, $hobby: String!){      addChef(name: $name, age: $age, hobby:$hobby){        id,        age,        name,        hobby      }    }

El fragmento anterior es un buen ejemplo de cómo podemos modificar nuestra consulta de mutación para utilizar variables de entrada en lugar de los valores estáticos anteriores. Las variables se declaran utilizando el $signo y luego el nombre que queremos darle a la variable. A continuación, establecemos explícitamente un tipo para la variable. Por ejemplo, la $namevariable se estableció en a String. El !después de la cadena muestra que el campo o la variable son obligatorios.

Al igual que en el ejemplo anterior, también podemos darle a la mutación un nombre único. El fragmento de código que aparece a continuación muestra una mutación con nombre y valores dinámicos de variables.

mutation mutateChef($name: String!, $age: Int!, $hobby: String!){  addChef(name: $name, age: $age, hobby:$hobby){    id,    age,    name,    hobby  }}

Construyendo una aplicación

Ahora que entendemos qué son las mutaciones, podemos crear una mini aplicación GraphQL para brindar una experiencia más práctica.

La aplicación que crearemos debe permitirnos realizar algunas operaciones CRUD básicas en un conjunto de datos ficticios que contengan los nombres de algunos chefs. En términos más claros, la aplicación le mostrará cómo crear, actualizar y eliminar un chef en particular de una matriz de chefs utilizando la tan comentada mutación GraphQL.

Configuración de un servidor de desarrollo

Antes de sumergirnos en la aplicación, necesitamos configurar un servidor al que enviar solicitudes.

En primer lugar, inicialice un proyecto vacío con npm init -y. Luego proceda a instalar todas las dependencias necesarias para la aplicación:

  1. npm i express express-graphql app-root-path

Una vez completada la instalación, cree un configdirectorio y luego cree un port.jsarchivo en el directorio y luego actualícelo con el siguiente código:

configuración/puerto.js

export const PORT = process.env.PORT || 9091

Creación de otros archivos de proyecto

En el directorio raíz, crea un app.jsarchivo que servirá como archivo de entrada a la aplicación. Luego, actualízalo con el código que se muestra a continuación:

aplicación.js

import express from 'express';import graphqlHTTP from 'express-graphql';import { PORT } from './config/port';import logger from './config/winston';const app = express();app.use(cors())app.use('/graphql', graphqlHTTP({   graphiql: true}))app.listen(PORT, () = {  logger.info(`Server now listening for request at port ${PORT}`);})

Finalmente ve a tu package.jsonarchivo y crea un devscript con el código a continuación:

paquete.json

"dev": "nodemon --exec babel-node app.js"

Este comando de script nos permite iniciar rápidamente el servidor local de nuestra aplicación sin tener que escribir tantos comandos de terminal.

Creación de la lógica y la API de la aplicación

En el directorio raíz, crea un Serverdirectorio. Luego, crea también un directorio modely un schemadirectorio. En el modeldirectorio, crea un index.jsarchivo y actualízalo con el código que se muestra a continuación:

/servidor/modelo/index.js

export const chefs = [  {    id: 1,    name: "Monique Black"  },  {    id: 2,    name: "Chidinma Madukwe"  } ]

Aquí, estamos exportando una matriz que chefscontiene los datos simulados que usaremos para representar a los chefs iniciales que tenemos en esta aplicación. Una vez hecho esto, crea un index.jsarchivo en el schemadirectorio y actualízalo con el código que se muestra a continuación:

./servidor/esquema/index.js

import {  GraphQLObjectType,  GraphQLString,  GraphQLID,  GraphQLList,  GraphQLSchema,  GraphQLNonNull  } from 'graphql'import { chefs } from '../model/'const ChefType = new GraphQLObjectType({  name: 'chef',  fields: () = ({    id: { type: GraphQLID },    name: { type: GraphQLString}  })})const RootQuery = new GraphQLObjectType({  name: 'RootQuery',  fields: {    chefs: {      type: new GraphQLList(ChefType),      resolve() {        return chefs      }    }  }})const Mutations = new GraphQLObjectType({  name: 'Mutation',  fields: {    deleteChef: {      type: ChefType,      args: {        id: { type: new GraphQLNonNull(GraphQLID)}      },      resolve(parent, args) {        const returnedData = chefs.filter(chef = {          return chef.id == args.id        })        return returnedData[0]      }    },    addChef: {      type: new GraphQLList(ChefType),      args: {        name: {type: new GraphQLNonNull(GraphQLString)}      },      resolve(parent, args) {        args.id = Math.floor(Math.random() * 10)        return [...chefs, args]      }    },    updateChef: {      type: ChefType,      args: {        id: { type: new GraphQLNonNull(GraphQLID)},        name: { type: new GraphQLNonNull(GraphQLString)}      },      resolve(parent, args) {        const updateChef = chefs.find(data = {          return data.id == args.id        })        updateChef.name = args.name        return updateChef      }    }  }})export const schema = new GraphQLSchema({    query: RootQuery,    mutation: Mutations})

Aunque se trata de un archivo de código extenso, incluye todo lo que hemos tratado en el tutorial. Las únicas cosas que se incluyeron pero que no se trataron anteriormente son los tipos GraphQL, que son:

  • GraphQLObjectType: lo que usamos para describir la estructura de una consulta o de un objeto de mutación.

  • GraphQLNonNullGraphQLNonNull: esto le indica a GraphQL que, si falta un archivo con un tipo en una consulta, dicha consulta o mutación no se realizará. El campo se convierte en un campo obligatorio.

  • GraphQLID: esto permite realizar una consulta si idse pasa un parámetro como una cadena o un entero.

  • GraphQLList: esto permite que GraphQL sepa que la respuesta contendrá datos de un tipo similar en una matriz. Los tipos de GraphQL no se limitan a estos, el resto se puede encontrar en el sitio web oficial de GraphQL.

Por último, regrese al app.jsarchivo y actualícelo con el código siguiente:

aplicación.js

import express from 'express';import graphqlHTTP from 'express-graphql';import cors from 'cors';import { PORT } from './config/port';import logger from './config/winston';import {schema} from './server/schema/';const app = express();app.use(cors())app.use('/graphql', graphqlHTTP({  schema,  graphiql: true}))app.listen(PORT, () = {  logger.info(`Server now listening for request at port ${PORT}`);})

Iniciando el servidor

Después de obtener el código, ejecute el npm run devcomando de script en su terminal para iniciar el servidor local. Debería recibir este mensaje registrado en la consola cuando su servidor se esté ejecutando correctamente.

"message":"Server now listening for request at port 9090","level":"info"}

Prueba de mutaciones en Graphiql

Ahora que tenemos un servidor, probemos nuestra aplicación con Graphiql. Graphiql es una aplicación GUI que se envía o se integra en el express-graphqlmódulo que instalamos anteriormente. Para abrirla, vaya a su navegador y visite esta URL http://localhost:9091/graphql. Debería ver una página similar a la que se muestra a continuación:

Para crear una consulta que devuelva todo chefsnuestro conjunto de datos existente, actualice el lado izquierdo de la GUI con el fragmento a continuación:

{  chefs {    id,    name  }}

Ahora haga clic en el botón Reproducir en la GUI y tendrá el mismo resultado que el siguiente:

Como se esperaba, devuelve todos los datos estáticos que tenemos actualmente en nuestra chefsmatriz.

Creando un nuevo chef

Para crear una mutación que agregue un nuevo chef al conjunto de datos de chefs existente, pasamos un nuevo nombre a la addCheffunción que definimos en el esquema. A continuación, se muestra un código de ejemplo para agregar un nuevo chef con el nombre "Chinwe Eze":

mutation{  addChef(name: "Chinwe Eze"){    id,    name  }}

Puede verificar esta funcionalidad ejecutando este fragmento en la GUI de Graphiql; debería obtener el mismo comportamiento que el mío a continuación:

Si te preguntas por qué tenemos un idID 6para el nuevo chef, es porque generamos identificadores aleatorios para cada chef nuevo. Puedes volver a consultar el esquema para verificar esta función.

Actualizar un Chef existente

Si quisiéramos actualizar los datos existentes, podríamos redefinir nuestra mutación y utilizar la updateCheffunción que definimos en el esquema para modificar los datos existentes. Un caso de uso típico sería el de un usuario que desea actualizar la información de su perfil.

En nuestro caso, si quisiéramos actualizar el nombre de un chef existente, todo lo que tenemos que hacer es pasar el nombre iddel chef con el nuevo nombre a la updateCheffunción. Actualicemos el nombre del chef Monique Blacka Simona White:

Notarás que esta mutación es bastante diferente del resto porque acepta dos argumentos. Sin embargo, una función de mutación no se limita a uno o dos argumentos, puede aceptar tantos como quieras. Lo único que debes tener en cuenta es si estás pasando los argumentos correctos.

Eliminar un Chef

Al igual que cualquier otra operación anterior que hayamos realizado con nuestra mutación, podemos eliminar datos existentes. En nuestro caso, eliminaremos un chef de nuestra matriz de chefs existente. Nuevamente, podemos pasar el nombre idde un chef a la deleteCheffunción que definimos en el esquema y el chef se eliminará.

Conclusión

En esta publicación, analizamos en profundidad las mutaciones de GraphQL. Demostramos conceptos como el diseño de esquemas de mutación, la denominación y prueba de mutaciones, el paso de variables de consulta a nuestras mutaciones, etc. Fuimos más allá y creamos un miniproyecto de GraphQL para brindarle un ejemplo más práctico sobre cómo trabajar con mutaciones de GraphQL en aplicaciones del mundo real.

En conclusión, Mutations nos proporciona una forma muy flexible de realizar la CUD en CRUD (Crear, Leer, Actualizar, Eliminar) sin tener que pasar por consultas de datos complejas o lógica de programación. Una vez que se ha creado un esquema y existe una relación entre los datos relacionados, no hay que hacer mucha programación para obtener los datos necesarios.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion