GraphQL es una nueva y emocionante API para consultas y manipulación ad hoc. Es extremadamente flexible y proporciona muchos beneficios. Es particularmente adecuado para exponer datos organizados como gráficos y árboles. Facebook desarrolló GraphQL en 2012 y lo abrió en 2015.
Despegó rápidamente y se convirtió en una de las tecnologías más novedosas. Muchas empresas innovadoras adoptaron y utilizaron GraphQL en la producción. En este tutorial aprenderás:
GraphQL se encuentra en su mejor momento cuando sus datos están organizados en una jerarquía o en un gráfico y el extremo frontal le gustaría acceder a diferentes subconjuntos de esta jerarquía o gráfico. Considere una aplicación que expone la NBA. Tienes equipos, jugadores, entrenadores, campeonatos y mucha información sobre cada uno. Aquí hay algunas consultas de muestra:
Podría llegar a cientos de tales consultas. Imagine que tiene que diseñar una API para exponer todas estas consultas al frente y poder extender fácilmente la API con nuevos tipos de consulta a medida que sus usuarios o gerente de producto presentan nuevas cosas interesantes para consultar..
Esto no es trivial. GraphQL fue diseñado para resolver este problema exacto, y con un solo punto final de API proporciona un enorme poder, como verá pronto.
Antes de sumergirse en las tuercas y tornillos de GraphQL, comparémoslo con REST, que actualmente es el tipo de API web más popular..
REST sigue un modelo orientado a los recursos. Si nuestros recursos son jugadores, entrenadores y equipos, entonces probablemente habrá puntos finales como:
A menudo, los puntos finales sin id solo devuelven una lista de identificadores, y los endpoints con el identificador devuelven la información completa en un recurso. Por supuesto, puede diseñar su API de otras maneras (por ejemplo, el punto final / players puede devolver también el nombre de cada jugador o toda la información sobre cada jugador).
El problema con este enfoque en un entorno dinámico es que no está obteniendo información (por ejemplo, solo obtiene los identificadores y necesita más información) o si está realizando búsquedas excesivas (por ejemplo, obtiene la información completa de cada jugador cuando está solo interesado en el nombre).
Esos son problemas difíciles. Cuando recuperas la información, si obtienes 100 ID, deberás realizar 100 llamadas API por separado para obtener la información de cada jugador. Cuando se realiza una búsqueda excesiva, se pierde mucho tiempo de back-end y ancho de banda de red al preparar y transferir una gran cantidad de datos que no son necesarios.
Hay formas de abordarlo con REST. Puede diseñar una gran cantidad de puntos finales personalizados, cada uno de los cuales devuelve exactamente los datos que necesita. Esta solución no es escalable. Es difícil mantener la API consistente. Es difícil evolucionarlo. Es difícil documentarlo y usarlo. Es difícil mantenerlo cuando hay mucha superposición entre los puntos finales personalizados.
Considere estos puntos finales adicionales:
Otro enfoque es mantener un pequeño número de puntos finales genéricos, pero proporcionar una gran cantidad de parámetros de consulta. Esta solución evita el problema de muchos puntos finales, pero va contra el grano del modelo REST, y también es difícil evolucionar y mantener constantemente.
Se podría decir que GraphQL ha llevado este enfoque al límite. No piensa en términos de recursos bien definidos, sino en términos de sub-gráficos de todo el dominio..
GraphQL modela el dominio utilizando un sistema de tipos que consta de tipos y atributos. Cada atributo tiene un tipo. El tipo de atributo puede ser uno de los tipos básicos que GraphQL proporciona como ID, String y Boolean, o un tipo definido por el usuario. Los nodos del gráfico son los tipos definidos por el usuario y los bordes son los atributos que tienen tipos definidos por el usuario.
Por ejemplo, si un tipo de "Jugador" tiene un atributo de "equipo" con el tipo de "Equipo", entonces significa que hay un borde entre cada nodo de jugador a un nodo de equipo. Todos los tipos se definen en un esquema que describe el modelo de objetos del dominio GraphQL.
Aquí hay un esquema muy simplificado para el dominio NBA. El jugador tiene un nombre, un equipo con el que está más asociado (sí, sé que los jugadores a veces se mueven de un equipo a otro) y la cantidad de campeonatos que ganó el jugador..
El equipo tiene un nombre, una gran variedad de jugadores y el número de campeonatos que ganó el equipo..
tipo Player id: ID name: String! equipo: equipo! campeonatoCuenta: Integer! escribe Team id: ID name: String! jugadores: [Player!]! campeonatoCuenta: Integer!
También hay puntos de entrada predefinidos. Esos son la consulta, la mutación y la suscripción. El extremo delantero se comunica con el extremo trasero a través de los puntos de entrada y los personaliza para sus necesidades.
Aquí hay una consulta que simplemente devuelve a todos los jugadores:
tipo Query allPlayers: [Player!]!
El signo de exclamación significa que el valor no puede ser nulo. En el caso de la todos los jugadores
Consulta, puede devolver una lista vacía, pero no nula. Además, significa que no puede haber un jugador nulo en la lista (¡ya que contiene Player!).
Aquí hay un servidor GraphQL de pleno derecho basado en node-express. Tiene un almacén de datos codificado en memoria. Normalmente, los datos estarán en una base de datos o se buscarán en otro servicio. Los datos se definen aquí (pida disculpas por adelantado si su equipo o jugador favorito no lo logró):
let data = "allPlayers": "1": "id": "1", "name": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2": "id": "2", "name": "Michael Jordan", "championshipCount": 6, "teamId": "1", "3": "id": "3", "name": "Scottie Pippen", "championshipCount": 6, "teamId": "1", "4": "id": "4", "name": "Magic Johnson", "championshipCount": 5, "teamId ":" 2 "," 5 ": " id ":" 5 "," nombre ":" Kobe Bryant "," championshipCount ": 5," teamId ":" 2 "," 6 ": " id ":" 6 "," name ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 ", "name": "Chicago Bulls", "championshipCount": 6, "players": [], "2": "id": "2", "name": "Los Angeles Lakers", "championshipCount": 16, "jugadores": [], "3": "id": "3", "nombre": "Golden State Warriors", "championshipCount": 5, "players": []
Las bibliotecas que utilizo son:
const express = require ('express'); const graphqlHTTP = require ('express-graphql'); const app = express (); const buildSchema = require ('graphql'); const _ = require ('lodash / core');
Este es el código para construir el esquema. Tenga en cuenta que he añadido un par de variables a la todos los jugadores
consulta de raíz.
schema = buildSchema ('tipo Player id: nombre de ID: String! championshipCount: Int! team: Team! tipo Team id: ID name: String! championshipCount: Int! jugadores: [Player!]! type Query allPlayers (desplazamiento: Int = 0, límite: Int = -1): [Player!]! '
Aquí viene la parte clave: conectar las consultas y en realidad servir los datos. los valor raíz
objeto puede contener múltiples raíces.
Aquí, sólo hay la todos los jugadores
. Extrae el desplazamiento y el límite de los argumentos, corta los datos de todos los jugadores y luego establece el equipo en cada jugador en función de la identificación del equipo. Esto hace que cada jugador sea un objeto anidado..
rootValue = allPlayers: (args) => offset = args ['offset'] limit = args ['limit'] r = _.values (data ["allPlayers"]). slice (offset) if (limit> - 1) r = r.slice (0, Math.min (límite, r.length)) _.forEach (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) return r,
Finalmente, aquí está el Graphql
punto final, pasando el esquema y el objeto de valor raíz:
app.use ('/ graphql', graphqlHTTP (schema: schema, rootValue: rootValue, graphiql: true)); app.listen (3000); module.exports = aplicación;
Ajuste grafiql
a cierto
nos permite probar el servidor con un increíble IDE de GraphQL en el navegador. Lo recomiendo encarecidamente para experimentar con diferentes consultas..
Todo está establecido. Naveguemos hasta http: // localhost: 3000 / graphql y divirtámonos..
Podemos comenzar simple, con solo una lista de los nombres de los jugadores:
query justNames allPlayers name Salida: "data": "allPlayers": ["name": "Stephen Curry", "name": "Michael Jordan", "name": "Scottie Pippen ", " nombre ":" Magic Johnson ", " nombre ":" Kobe Bryant ", " nombre ":" Kevin Durant "]
Bien. Tenemos algunas superestrellas aquí. Sin duda. Vayamos por algo más sofisticado: a partir de offset 4, obtenga 2 jugadores. Para cada jugador, devuelva su nombre y cuántos campeonatos ganó, así como el nombre de su equipo y cuántos campeonatos ganó el equipo.
consulta twoPlayers allPlayers (offset: 4, limit: 2) name championshipCount team name championshipCount Salida: "data": "allPlayers": ["name": "Kobe Bryant", "championshipCount": 5, "equipo": "nombre": "Los Angeles Lakers", "championshipCount": 16, "nombre": "Kevin Durant", "championshipCount": 1, "equipo": "nombre": "Golden State Warriors", "championshipCount": 5]
Así que Kobe Bryant ganó cinco campeonatos con los Lakers, que ganaron 16 campeonatos en total. Kevin Durant ganó solo un campeonato con los Warriors, quienes ganaron cinco campeonatos en total.
Magic Johnson fue un mago en la cancha seguro. Pero no podría haberlo hecho sin su amigo Kareem Abdul-Jabbar. Agreguemos Kareem a nuestra base de datos. Podemos definir mutaciones de GraphQL para realizar operaciones como agregar, actualizar y eliminar datos de nuestro gráfico.
Primero, agreguemos un tipo de mutación al esquema. Se parece un poco a una firma de función:
type Mutation createPlayer (name: String, championshipCount: Int, teamId: String): Player
Entonces, necesitamos implementarlo y agregarlo al valor raíz. La implementación simplemente toma los parámetros proporcionados por la consulta y agrega un nuevo objeto a la datos ['allPlayers']
. También se asegura de configurar el equipo correctamente. Finalmente, devuelve el nuevo jugador..
createPlayer: (args) => id = (_.values (data ['allPlayers']). length + 1) .toString () args ['id'] = id args ['team'] = data ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args return data [' allPlayers '] [id],
Para agregar Kareem, podemos invocar la mutación y consultar al jugador devuelto:
mutación addKareem createPlayer (nombre: "Kareem Abdul-Jabbar", championshipCount: 6, teamId: "2") name championshipCount team name Salida: "data": "createPlayer": "name": "Kareem Abdul-Jabbar", "championshipCount": 6, "team": "name": "Los Angeles Lakers"
Aquí hay un pequeño secreto sobre las mutaciones ... en realidad son exactamente lo mismo que las consultas. Puede modificar sus datos en una consulta y puede devolver datos de una mutación. GraphQL no va a echar un vistazo a su código. Tanto las consultas como las mutaciones pueden tomar argumentos y devolver datos. Es más como azúcar sintáctica hacer que su esquema sea más legible para los humanos.
Las suscripciones son otra característica importante de GraphQL. Con las suscripciones, el cliente puede suscribirse a eventos que se activarán cada vez que cambie el estado del servidor. Las suscripciones se introdujeron en una etapa posterior y se implementan en diferentes marcos de diferentes maneras..
GraphQL verificará cada consulta o mutación contra el esquema. Esta es una gran ganancia cuando los datos de entrada tienen una forma compleja. No tienes que escribir un código de validación molesto y quebradizo. GraphQL se hará cargo de ti por ti..
Puede inspeccionar y consultar el esquema actual. Eso te da meta-poderes para descubrir dinámicamente el esquema. Aquí hay una consulta que devuelve todos los nombres de tipo y su descripción:
consulta q __schema tipos nombre descripción
GraphQL es una nueva y emocionante tecnología API que proporciona muchos beneficios sobre las API REST. Hay una comunidad vibrante detrás de esto, sin mencionar Facebook. Predigo que se convertirá en un elemento básico en ningún momento. Darle una oportunidad. Te gustará.