Mutaciones y suscripciones en GraphQL

Índice
  1. Instalación
  2. Configuración estándar
  3. Crear mutación
  4. Eliminar y actualizar mutaciones
  5. Suscripciones
  6. Conclusión

En este artículo, veremos cómo usar los tipos Mutationy Subscriptionpara manipular y observar los datos en busca de cambios, en lugar de solo realizar consultas, en GraphQL. No dudes en descubrir más en la documentación oficial.

Para simplificar las cosas, no utilizaremos ninguna base de datos ni solicitudes HTTP, pero es necesario saber cómo configurar una API básica con esquemas y resolutores.

Instalación

Usaremos la biblioteca graphql-yoga para configurar nuestro servidor y nodemonhacer que se recargue automáticamente. También necesitaremos un preprocesador como Prepros o Babel para poder usar las últimas características de JavaScript.

$ npm i graphql-yoga nodemon

Configuración estándar

Además de nuestra configuración de servidor, solo tenemos una usersmatriz vacía y un esquema y solucionador simples para devolver todos nuestros usuarios.

servidor.js

import { GraphQLServer } from 'graphql-yoga'const users = [];const typeDefs = `  type Query {    users: [User!]!  }  type User {    name: String!    age: Int!  }`;const resolvers = {  Query: {    user() {      return users;    }  }}const server = new GraphQLServer({ typeDefs, resolvers });server.start(() = console.log('server running'));

Necesitaremos un startscript que ejecute nodemon en nuestro archivo de salida:

paquete.json

{  "name": "graphql-api",  "version": "1.0.0",  "description": "",  "main": "server.js",  "dependencies": {    "graphql-yoga": "^1.16.7"  },  "devDependencies": {    "nodemon": "^1.19.1"  },  "scripts": {    "start": "nodemon server-dist.js"  },  "author": "",  "license": "ISC"}

Ahora en la terminal puedes simplemente ejecutar npm run start.

localhost:4000Deberíamos tener GraphQL Playground en funcionamiento con una consulta para devolver user { name }nuestra matriz vacía.

Crear mutación

La sintaxis de nuestras mutaciones es casi la misma que la de nuestra consulta. Solo tenemos que declarar las opciones que queremos, agregar los argumentos (si los hay) y declarar el tipo que se debe devolver cuando se complete la operación.

En lugar de agregar todos nuestros argumentos en línea, es bastante común dividir los datos en su propio tipo especial llamado inputtipo para fines de organización. Es una convención de nomenclatura general que verá en herramientas como Prisma para nombrar la entrada como sea que el solucionador termine con la palabra entrada, por lo que addUserobtiene una AddUserInputentrada.

servidor.js

const typeDefs = `  type Mutation {    addUser(data: AddUserInput): User!  }  input AddUserInput {    name: String!,     age: Int!  }`;

Al igual que con las consultas, podemos acceder a los argumentos argsy agregar nuestro nuevo usuario a nuestra matriz y devolverlos.

const resolvers = {  Query: {...},  Mutation: {    addUser(parent, args, ctx, info) {      const user = { ...args.data };      users.push(user);      return user;    }  }}

Eliminar y actualizar mutaciones

Como la sintaxis es tan simple, resulta casi sin esfuerzo desarrollar las demás operaciones CRUD.

Sabremos qué elemento estamos eliminando o actualizando simplemente buscando al usuario por nombre.

servidor.js

const typeDefs = `  type Mutation {    deleteUser(name: String!): User!    updateUser(name: String!, data: UpdateUserInput): User!  }  input UpdateUserInput {    name: String    age: Int  }`const resolvers = {  Query: { ... },  Mutation: {    deleteUser(parent, args, ctx, info) {      // We're just finding the index of the user with a matching name,      // checking if it exists, and removing that section of the array.      const userIndex = users.findIndex(user = user.name.toLowerCase() === args.name.toLowerCase());      if (userIndex === -1) throw new Error('User not found');      const user = users.splice(userIndex, 1);      return user[0];    },    updateUser(parent, args, ctx, info) {      const user = users.find(user = user.name.toLowerCase() === args.who.toLowerCase());      if (!user) throw new Error('User not found');      // This way, only the fields that are passed-in will be changed.      if (typeof args.data.name === "string") user.name = args.data.name;      if (typeof args.data.age !== "undefined") user.age = args.data.age;      return user;    }  }}

Ahora localhost:4000puedes probar esta mutación y consultar nuestra matriz nuevamente.

mutation {  addUser(data: {    name: "Alli",    age: 48  }) {    name    age  }}

O en otra pestaña:

mutation {  updateUser(name: "Alli", data: {    name: "Crusher",    age: 27  }) {    name    age  }}

Suscripciones

Podemos usar el Subscriptiontipo especial para poder observar cualquier cambio en nuestros datos. La sintaxis es muy similar a la de las consultas y mutaciones, solo agrega el tipo Subscription, agrega lo que quieras que observe y lo que quieras que se devuelva. Vamos a devolver un tipo personalizado que nos enviará de vuelta nuestros datos modificados y nos dirá si fue una operación de creación, eliminación o actualización.

Para usar suscripciones, vamos a tener que usar PubSubgraphql-yoga e inicializarlo antes que todo lo demás. En nuestro solucionador de suscripciones, usaremos una función llamada subscribeque deberá devolver un evento asincrónico, al que llamaremos user. Siempre que queramos conectar algo a esta suscripción, usaremos este nombre de evento.

servidor.js

import { GraphQLServer, PubSub } from 'graphql-yoga';const pubsub = new PubSub();const typeDefs = `type Subscription {  user: UserSubscription!}type UserSubscription {  mutation: String!  data: User!}`const resolvers = {  Query: { ... },  Mutation: { ... },  Subscription: {    user: {      subscribe() {        return pubsub.asyncIterator('user');      }    }}}

Nuestra suscripción está configurada y disponible en los documentos GraphQL generados, pero no sabe cuándo activarse ni qué devolver. En nuestras mutaciones, agregaremos pubsub.publishun enlace a nuestro userevento y pasaremos nuestros datos.

const resolvers = {  Query: { ... },  Mutation: {    addUser(parent, args, ctx, info) {      const user = { ...args.data };      users.push(user);      // We'll just link it to our user event,      // and return what type of mutation this is and our new user.      pubsub.publish("user", {        user: {          mutation: "Added",          data: user        }      });      return user;    },    deleteUser(parent, args, ctx, info) {      const userIndex = users.findIndex(        user = user.name.toLowerCase() === args.who.toLowerCase()      );      if (userIndex === -1) throw new Error("User not found");      const user = users.splice(userIndex, 1);      pubsub.publish("user", {        user: {          mutation: "Deleted",          data: user[0]        }      });      return user[0];    },    updateUser(parent, args, ctx, info) {      const user = users.find(        user = user.name.toLowerCase() === args.who.toLowerCase()      );      if (!user) throw new Error("User not found");      if (typeof args.data.name === "string") user.name = args.data.name;      if (typeof args.data.age !== "undefined") user.age = args.data.age;      pubsub.publish("user", {        user: {          mutation: "Updated",          data: user        }      });      return user;    }  },  Subscription: { ... }};

En la localhost:4000pestaña podemos abrir una nueva y ejecutar la siguiente suscripción. Deberías ver un mensaje que diga "escuchando..." con una pequeña rueda giratoria. En otra pestaña, podemos ejecutar cualquiera de nuestras otras mutaciones anteriores y nuestra suscripción devolverá automáticamente lo que se hizo y lo que se modificó.

subscription {  user {    mutation    data {      name      age    }  }}

Conclusión

Espero que esto haya sido útil para comprender cómo configurar las API de GraphQL con mutaciones y suscripciones. Si tuvo algún problema al configurar esto, siempre puede consultar este repositorio.

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