Introducción a GraphQL: la evolución del diseño de API (Parte 2)

 

 

 

  • Implemente rápidamente. Implementar inteligentemente
  • Clase magistral de tipografía, con Elliot Jay Stocks

  • Índice
    1. Tráfico HTTP mínimo
    2. Cargas útiles mínimas
    3. Legible por humanos
    4. Herramientas ricas
    5. Preservación del razonamiento local
  • ¿Qué es GraphQL?
    1. GraphQL es una especificación (no una implementación)
    2. La especificación de GraphQL no menciona clientes ni datos
    3. Divulgación progresiva de la complejidad
  • Una aplicación de ejemplo
    1. Usando una API REST
    2. Usando una API GraphQL
  • Defectos en GraphQL
    1. Integridad de los datos
    2. Manejo de errores complejos
    3. Almacenamiento en caché
    4. Alto costo de CPU
  • Pensamientos finales
    1. Otras lecturas
  • Las características de GraphQL no son una revolución, pero lo que hace que GraphQL sea poderoso es que el nivel de pulido, integración y facilidad de uso lo hacen más que la suma de sus partes. Muchas de las cosas que GraphQL logra se pueden lograr, con esfuerzo y disciplina, utilizando REST o RPC, pero GraphQL ofrece API de última generación para una enorme cantidad de proyectos que pueden no tener el tiempo, los recursos o las herramientas para hacerlo por sí mismos. En este artículo, Eric Baer se centra casi por completo en por qué existe GraphQL y los problemas que resuelve.

     

    En la Parte 1 analizamos cómo han evolucionado las API en las últimas décadas y cómo cada una dio paso a la siguiente. También hablamos sobre algunos de los inconvenientes particulares de utilizar REST para el desarrollo de clientes móviles. En este artículo, quiero analizar hacia dónde parece dirigirse el diseño de API de clientes móviles, con especial énfasis en GraphQL.

    Por supuesto, hay muchas personas, empresas y proyectos que han intentado abordar las deficiencias de REST a lo largo de los años: HAL, Swagger/OpenAPI, OData JSON API y docenas de otros proyectos más pequeños o internos han tratado de poner orden en el sistema. mundo de REST sin especificaciones. En lugar de tomar el mundo tal como es y proponer mejoras incrementales, o intentar ensamblar suficientes piezas dispares para convertir REST en lo que necesito, me gustaría probar un experimento mental. Una vez comprendidas las técnicas que han funcionado y las que no han funcionado en el pasado, me gustaría aprovechar las limitaciones actuales y nuestros lenguajes inmensamente más expresivos para intentar esbozar la API que queremos. Trabajemos desde la experiencia del desarrollador hacia atrás en lugar de la implementación hacia adelante (te estoy mirando SQL).

    Tráfico HTTP mínimo

    Sabemos que el costo de cada solicitud de red (HTTP/1) es alto en bastantes medidas, desde la latencia hasta la duración de la batería. Idealmente, los clientes de nuestra nueva API necesitarán una forma de solicitar todos los datos que necesitan en el menor número de viajes de ida y vuelta posible.

    Cargas útiles mínimas

    También sabemos que el cliente promedio tiene recursos limitados, en ancho de banda, CPU y memoria, por lo que nuestro objetivo debe ser enviar solo la información que nuestro cliente necesita. Para hacer esto, probablemente necesitaremos una forma para que el cliente solicite datos específicos.

    Legible por humanos

    Aprendimos de los días de SOAP que no es fácil interactuar con una API, la gente haría una mueca al mencionarla. Los equipos de ingeniería quieren utilizar las mismas herramientas en las que hemos confiado durante años curl, como wgety Charlesla pestaña de red de nuestros navegadores.

    Herramientas ricas

    Otra cosa que aprendimos de XML-RPC y SOAP es que los contratos cliente/servidor y los sistemas de tipos, en particular, son increíblemente útiles. Si es posible, cualquier API nueva tendría la ligereza de un formato como JSON o YAML con la capacidad de introspección de contratos más estructurados y con seguridad de tipos.

     

    Preservación del razonamiento local

    A lo largo de los años, hemos llegado a un acuerdo sobre algunos principios rectores sobre cómo organizar grandes bases de código; el principal es la "separación de preocupaciones". Desafortunadamente, para la mayoría de los proyectos, esto tiende a fallar en forma de una capa de acceso a datos centralizada. Si es posible, las diferentes partes de una aplicación deberían tener la opción de gestionar sus propias necesidades de datos junto con otras funciones.

    Dado que estamos diseñando una API centrada en el cliente, comencemos con cómo se vería recuperar datos en una API como esta. Si sabemos que necesitamos hacer viajes de ida y vuelta mínimos y que necesitamos poder filtrar los campos que no queremos, necesitamos una manera de recorrer grandes conjuntos de datos y solicitar solo las partes que sean necesarias. útil para nosotros. Parece que un lenguaje de consulta encajaría bien aquí.

    No necesitamos hacer preguntas sobre nuestros datos de la misma manera que se hace con una base de datos, por lo que un lenguaje imperativo como SQL parece la herramienta equivocada. De hecho, nuestros objetivos principales son atravesar relaciones preexistentes y limitar campos que deberíamos poder hacer con algo relativamente simple y declarativo. La industria se ha decidido por JSON para datos no binarios, así que comencemos con un lenguaje de consulta declarativo similar a JSON. Deberíamos poder describir los datos que necesitamos y el servidor debería devolver JSON que contenga esos campos.

    La solicitud y respuesta de una 'API de ensueño'. ( Vista previa grande )

    Un lenguaje de consulta declarativo cumple con el requisito de cargas útiles mínimas y tráfico HTTP mínimo, pero existe otro beneficio que nos ayudará con otro de nuestros objetivos de diseño. Muchos lenguajes declarativos, de consulta y otros, pueden manipularse eficientemente como si fueran datos. Si diseñamos con cuidado, nuestro lenguaje de consulta permitirá a los desarrolladores separar solicitudes grandes y recombinarlas de cualquier forma que tenga sentido para su proyecto. Usar un lenguaje de consulta como este nos ayudaría a avanzar hacia nuestro objetivo final de Preservación del Razonamiento Local.

    Hay muchas cosas interesantes que puede hacer una vez que sus consultas se conviertan en "datos". Por ejemplo, podría interceptar todas las solicitudes y agruparlas de forma similar a cómo un DOM virtual agrupa las actualizaciones de DOM; también podría usar un compilador para extraer las consultas pequeñas en el momento de la compilación para almacenar en caché previamente los datos o podría crear un sistema de caché sofisticado. como Apollo Cache.

    Fusionar varias consultas en una sola solicitud. ( Vista previa grande )

    El último elemento de la lista de deseos de API son las herramientas. Ya obtenemos algo de esto usando un lenguaje de consulta, pero el verdadero poder viene cuando lo combinas con un sistema de tipos. Con un esquema escrito simple en el servidor, existen posibilidades casi infinitas de herramientas enriquecidas. Las consultas se pueden analizar y validar estáticamente según el contrato, las integraciones IDE pueden proporcionar sugerencias o autocompletado, los compiladores pueden optimizar el tiempo de compilación de las consultas o se pueden unir múltiples esquemas para formar una superficie API contigua.

     

    Un ejemplo de cómo podría verse el sistema de tipos de una API. ( Vista previa grande )

    Diseñar una API que combine un lenguaje de consulta y un sistema de tipos puede parecer una propuesta dramática, pero la gente ha estado experimentando con esto, de diversas formas, durante años. XML-RPC impulsó las respuestas escritas a mediados de los años 90 y su sucesor, SOAP, dominó durante años. Más recientemente, hay cosas como la abstracción MongoDB de Meteor, Horizon de RethinkDB (RIP), el increíble Falcor de Netflix que han estado usando para Netflix.com durante años y por último está GraphQL de Facebook. Durante el resto de este ensayo, me centraré en GraphQL ya que, si bien otros proyectos como Falcor están haciendo cosas similares, la mentalidad compartida de la comunidad parece favorecerlo abrumadoramente.

    ¿Qué es GraphQL?

    Primero tengo que decir que mentí un poco. La API que construimos anteriormente fue GraphQL. GraphQL es solo un sistema de tipos para sus datos, un lenguaje de consulta para recorrerlos; el resto es solo detalle. En GraphQL, usted describe sus datos como un gráfico de interconexiones y su cliente solicita específicamente el subconjunto de datos que necesita. Se habla y escribe mucho sobre todas las cosas increíbles que permite GraphQL, pero los conceptos centrales son muy manejables y sencillos.

    Para hacer que estos conceptos sean más concretos y ayudar a ilustrar cómo GraphQL intenta abordar algunos de los problemas en la Parte 1 , el resto de esta publicación creará una API GraphQL que puede impulsar el blog en la Parte 1 de esta serie. Antes de pasar al código, hay algunas cosas que se deben tener en cuenta sobre GraphQL.

    GraphQL es una especificación (no una implementación)

    GraphQL es solo una especificación. Define un sistema de tipos junto con un lenguaje de consulta simple, y eso es todo. Lo primero que se desprende de esto es que GraphQL no está, de ninguna manera, vinculado a un lenguaje en particular. Hay más de dos docenas de implementaciones en todo, desde Haskell hasta C++, de las cuales JavaScript es solo una. Poco después de que se anunciara la especificación, Facebook lanzó una implementación de referencia en JavaScript pero, como no la usan internamente, las implementaciones en lenguajes como Go y Clojure pueden ser incluso mejores o más rápidas.

    La especificación de GraphQL no menciona clientes ni datos

    Si lees las especificaciones, notarás que hay dos cosas notoriamente ausentes. En primer lugar, más allá del lenguaje de consulta, no se mencionan las integraciones de clientes. Herramientas como Apollo, Relay, Loka y similares son posibles gracias al diseño de GraphQL, pero de ninguna manera forman parte ni son necesarias para su uso. En segundo lugar, no se menciona ninguna capa de datos en particular. El mismo servidor GraphQL puede, y frecuentemente lo hace, recuperar datos de un conjunto heterogéneo de fuentes. Puede solicitar datos almacenados en caché de Redis, realizar una búsqueda de direcciones desde la API de USPS y llamar a microservicios basados ​​en protobuff y el cliente nunca notará la diferencia.

     

    Divulgación progresiva de la complejidad

    Para muchas personas, GraphQL ha llegado a una rara intersección de poder y simplicidad. Hace un trabajo fantástico al hacer que las cosas simples sean simples y las difíciles posibles. Hacer que un servidor funcione y entregue datos escritos a través de HTTP puede requerir solo unas pocas líneas de código en prácticamente cualquier idioma que pueda imaginar.

    Por ejemplo, un servidor GraphQL puede empaquetar una API REST existente y sus clientes pueden obtener datos con solicitudes GET regulares tal como lo haría con otros servicios. Puedes ver una demostración aquí. O, si el proyecto necesita un conjunto de herramientas más sofisticado, es posible usar GraphQL para hacer cosas como autenticación a nivel de campo, suscripciones de publicación/suscripción o consultas precompiladas/almacenadas en caché.

    Una aplicación de ejemplo

    El objetivo de este ejemplo es demostrar el poder y la simplicidad de GraphQL en ~70 líneas de JavaScript, no escribir un tutorial extenso. No entraré en demasiados detalles sobre la sintaxis y la semántica, pero todo el código aquí se puede ejecutar y hay un enlace a una versión descargable del proyecto al final del artículo. Si después de pasar por esto, desea profundizar un poco más, tengo una colección de recursos en mi blog que lo ayudarán a crear servicios más grandes y sólidos. Blog sobre salud

    Para la demostración, usaré JavaScript, pero los pasos son muy similares en cualquier idioma. Comencemos con algunos datos de muestra utilizando el increíble Mocky.io.

    Autores

    { 9: { id: 9, name: "Eric Baer", company: "Formidable" }, ...}

    Publicaciones

    [ { id: 17, author: "author/7", categories: [ "software engineering" ], publishdate: "2016/03/27 14:00", summary: "...", tags: [ "http/2", "interlock" ], title: "http/2 server push" }, ...]

    El primer paso es crear un nuevo proyecto con expressel express-graphqlmiddleware.

    bashnpm init -y npm install --save graphql express express-graphql

    Y para crear un index.jsarchivo con un servidor express.

    const app = require("express")();const PORT = 5000;app.listen(PORT, () = { console.log(`Server running at https://localhost:${PORT}`);});

    Para empezar a trabajar con GraphQL, podemos empezar modelando los datos en la API REST. En un nuevo archivo llamado schema.jsagregue lo siguiente:

    const { GraphQLInt, GraphQLList, GraphQLObjectType, GraphQLSchema, GraphQLString} = require("graphql");const Author = new GraphQLObjectType({ name: "Author", fields: { id: { type: GraphQLInt }, name: { type: GraphQLString }, company: { type: GraphQLString }, }});const Post = new GraphQLObjectType({ name: "Post", fields: { id: { type: GraphQLInt }, author: { type: Author }, categories: { type: new GraphQLList(GraphQLString) }, publishDate: { type: GraphQLString }, summary: { type: GraphQLString }, tags: { type: new GraphQLList(GraphQLString) }, title: { type: GraphQLString } }});const Blog = new GraphQLObjectType({ name: "Blog", fields: { posts: { type: new GraphQLList(Post) } }});module.exports = new GraphQLSchema({ query: Blog});

    El código anterior asigna los tipos de las respuestas JSON de nuestra API a los tipos de GraphQL. A GraphQLObjectTypecorresponde a JavaScript Object, a GraphQLStringcorresponde a JavaScript Stringy así sucesivamente. El único tipo especial al que hay que prestar atención es el GraphQLSchemade las últimas líneas. Es GraphQLSchemala exportación de nivel raíz de un GraphQL: el punto de partida para que las consultas atraviesen el gráfico. En este ejemplo básico, solo estamos definiendo query; aquí es donde definirías mutaciones (escrituras) y suscripciones.

     

    A continuación, agregaremos el esquema a nuestro servidor express en el index.jsarchivo. Para hacer esto, agregaremos el express-graphqlmiddleware y le pasaremos el esquema.

    const graphqlHttp = require("express-graphql");const schema = require("./schema.js");const app = require("express")();const PORT = 5000;app.use(graphqlHttp({ schema, // Pretty Print the JSON response pretty: true, // Enable the GraphiQL dev tool graphiql: true}));app.listen(PORT, () = { console.log(`Server running at https://localhost:${PORT}`);});

    En este punto, aunque no devolvemos ningún dato, tenemos un servidor GraphQL en funcionamiento que proporciona su esquema a los clientes. Para facilitar el inicio de la aplicación, también agregaremos un script de inicio al archivo package.json.

    "scripts": { "start": "nodemon index.js"},

    Al ejecutar el proyecto y acceder a https://localhost:5000/ debería mostrarse un explorador de datos llamado GraphiQL. GraphiQL se cargará de forma predeterminada siempre que el Acceptencabezado HTTP no esté configurado en application/json. Llamar a esta misma URL con fetcho cURLusar application/jsondevolverá un resultado JSON. Siéntase libre de jugar con la documentación incorporada y escribir una consulta.

    Una captura de pantalla de GraphQL de GraphQL. ( Vista previa grande )

    Lo único que queda por hacer para completar el servidor es conectar los datos subyacentes al esquema. Para hacer esto, necesitamos definir resolvefunciones. En GraphQL, una consulta se ejecuta de arriba hacia abajo llamando a una resolvefunción mientras atraviesa el árbol. Por ejemplo, para la siguiente consulta:

    query homepage { posts { title }}

    GraphQL primero llamará posts.resolve(parentData)y luego posts.title.resolve(parentData). Comencemos por definir el solucionador en nuestra lista de publicaciones de blog.

    const Blog = new GraphQLObjectType({ name: "Blog", fields: { posts: { type: new GraphQLList(Post), resolve: () = { return fetch('https://www.mocky.io/v2/594a3ac810000053021aa3a7') .then((response) = response.json()) } } }});

    Estoy usando el isomorphic-fetchpaquete aquí para realizar una solicitud HTTP ya que demuestra muy bien cómo devolver una Promesa desde un solucionador, pero puedes usar lo que quieras. Esta función devolverá una serie de publicaciones del tipo Blog. La función de resolución predeterminada para la implementación JavaScript de GraphQL es parentData.fieldName. Por ejemplo, el solucionador predeterminado para el campo Nombre del autor sería:

     

    rawAuthorObject = rawAuthorObject.name

    Este solucionador de anulación única debe proporcionar los datos para todo el objeto de publicación. Aún necesitamos definir el solucionador para Autor, pero si ejecuta una consulta para obtener los datos necesarios para la página de inicio, debería verlo funcionando.

    Una captura de pantalla de una consulta básica realizada con GraphiQL. ( Vista previa grande )

    Dado que el atributo de autor en nuestra API de publicaciones es solo el ID del autor, cuando GraphQL busca un objeto que define el nombre y la empresa y encuentra una cadena, simplemente devolverá null. Para conectar el Autor, debemos cambiar nuestro esquema de publicación para que se vea como el siguiente:

    const Post = new GraphQLObjectType({ name: "Post", fields: { id: { type: GraphQLInt }, author: { type: Author, resolve: (subTree) = { // Get the AuthorId from the post data const authorId = subTree.author.split("/")[1]; return fetch('https://www.mocky.io/v2/594a3bd21000006d021aa3ac') .then((response) = response.json()) .then(authors = authors[authorId]); } }, ... }});

    Ahora, tenemos un servidor GraphQL completamente funcional que incluye una API REST. La fuente completa se puede descargar desde este enlace de Github o ejecutarla desde esta plataforma de lanzamiento GraphQL.

    Quizás se pregunte acerca de las herramientas que necesitará utilizar para consumir un punto final GraphQL como este. Hay muchas opciones como Relay y Apollo, pero para empezar, creo que el enfoque simple es el mejor. Si jugaste mucho con GraphiQL, es posible que hayas notado que tiene una URL larga. Esta URL es solo una versión codificada en URI de su consulta. Para crear una consulta GraphQL en JavaScript, puede hacer algo como esto:

    const homepageQuery = ` posts { title author { name } }`;const uriEncodedQuery = encodeURIComponent(homepageQuery);fetch(`https://localhost:5000/?query=${uriEncodedQuery}`);

    O, si lo desea, puede copiar y pegar la URL directamente desde GraphiQL de esta manera:

    https://localhost:5000/?query=query%20homepage%20%7B%0A%20%20posts%20%7B%0A%20%20%20%20title%0A%20%20%20%20author%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%7D%0A%20%20%7D%0A%7DoperationName=homepage

    Como tenemos un punto final GraphQL y una forma de usarlo, podemos compararlo con nuestra API RESTish. El código que necesitábamos escribir para recuperar nuestros datos usando una API RESTish tenía este aspecto:

    Usando una API REST

    const getPosts = () = fetch(`${API_ROOT}/posts`);const getPost = postId = fetch(`${API_ROOT}/post/${postId}`);const getAuthor = authorId = fetch(`${API_ROOT}/author/${postId}`);const getPostWithAuthor = post = { return getAuthor(post.author) .then(author = { return Object.assign({}, post, { author }) })};const getHomePageData = () = { return getPosts() .then(posts = { const postDetails = posts.map(getPostWithAuthor); return Promise.all(postDetails); })};

    Usando una API GraphQL

    const homepageQuery = ` posts { title author { name } }`;const uriEncodedQuery = encodeURIComponent(homepageQuery);fetch(`https://localhost:5000/?query=${uriEncodedQuery}`);

    En resumen, hemos utilizado GraphQL para:

     

    • Reducir nueve solicitudes (lista de publicaciones, cuatro publicaciones de blog y el autor de cada publicación).
    • Reducir en un porcentaje importante la cantidad de datos enviados.
    • Utilice increíbles herramientas de desarrollo para crear nuestras consultas.
    • Escriba un código mucho más limpio en nuestro cliente.

    Defectos en GraphQL

    Si bien creo que las exageraciones están justificadas, no existe una solución milagrosa y, por muy bueno que sea GraphQL, no está exento de fallas.

    Integridad de los datos

    GraphQL a veces parece una herramienta diseñada específicamente para obtener buenos datos. A menudo funciona mejor como una especie de puerta de enlace, uniendo servicios dispares o tablas altamente normalizadas. Si los datos que provienen de los servicios que consume son confusos y no estructurados, agregar un canal de transformación de datos debajo de GraphQL puede ser un verdadero desafío. El alcance de una función de resolución GraphQL son solo sus propios datos y los de sus hijos. Si una tarea de orquestación necesita acceso a datos de un hermano o padre en el árbol, puede resultar especialmente desafiante.

    Manejo de errores complejos

    Una solicitud GraphQL puede ejecutar una cantidad arbitraria de consultas y cada consulta puede afectar a una cantidad arbitraria de servicios. Si alguna parte de la solicitud falla, en lugar de fallar toda la solicitud, GraphQL, de forma predeterminada, devuelve datos parciales. Los datos parciales probablemente sean la opción correcta desde el punto de vista técnico y pueden resultar increíblemente útiles y eficientes. El inconveniente es que el manejo de errores ya no es tan simple como verificar el código de estado HTTP. Este comportamiento se puede desactivar, pero la mayoría de las veces, los clientes terminan con casos de error más sofisticados.

    Almacenamiento en caché

    Aunque suele ser una buena idea utilizar consultas GraphQL estáticas, para organizaciones como Github que permiten consultas arbitrarias, el almacenamiento en caché de red con herramientas estándar como Varnish o Fastly ya no será posible.

    Alto costo de CPU

    Analizar, validar y verificar el tipo de una consulta es un proceso vinculado a la CPU que puede provocar problemas de rendimiento en lenguajes de un solo subproceso como JavaScript.

    Esto es sólo un problema para la evaluación de consultas en tiempo de ejecución.

    Pensamientos finales

    Las funciones de GraphQL no son una revolución; algunas de ellas existen desde hace casi 30 años. Lo que hace que GraphQL sea poderoso es que el nivel de pulido, integración y facilidad de uso lo hacen más que la suma de sus partes.

    Muchas de las cosas que GraphQL logra se pueden lograr, con esfuerzo y disciplina, utilizando REST o RPC, pero GraphQL ofrece API de última generación para una enorme cantidad de proyectos que pueden no tener el tiempo, los recursos o las herramientas para hacerlo por sí mismos. También es cierto que GraphQL no es una solución milagrosa, pero sus defectos tienden a ser menores y bien comprendidos. Como alguien que ha creado un servidor GraphQL razonablemente complicado, puedo decir fácilmente que los beneficios superan fácilmente el costo.

    Este ensayo se centra casi por completo en por qué existe GraphQL y los problemas que resuelve. Si esto ha despertado su interés en aprender más sobre su semántica y cómo usarla, lo animo a que aprenda de la manera que mejor le funcione, ya sea blogs, youtube o simplemente leyendo la fuente ( How To GraphQL es particularmente bueno).

    Si disfrutó este artículo (o si lo odió) y desea enviarme su opinión, encuéntreme en Twitter como @ebaerbaerbaer o LinkedIn en ericjbaer .

    Otras lecturas

    • Knip: una herramienta automatizada para buscar archivos, exportaciones y dependencias no utilizados
    • Una guía para el kit de herramientas Redux con TypeScript
    • Por qué debería considerar los gráficos para su próximo proyecto GraphQL
    • Creación de una aplicación de notificación de precios de acciones utilizando React, Apollo GraphQL y Hasura

    (rb, ra, yk, il, mrn)Explora más en

    • javascript
    • API
    • GrafoQL





    Tal vez te puede interesar:

    1. Diseño de un cuadro de texto, íntegro
    2. Diseño y construcción de una aplicación web progresiva sin marco (Parte 3)
    3. Escribir un motor de aventuras de texto multijugador en Node.js: diseño del servidor Game Engine (Parte 2)
    4. Componentes de diseño en React

    Introducción a GraphQL: la evolución del diseño de API (Parte 2)

    Introducción a GraphQL: la evolución del diseño de API (Parte 2)

    Implemente rápidamente. Implementar inteligentemente Clase magistral de tipografía, con Elliot Jay Stocks Índice

    programar

    es

    https://aprendeprogramando.es/static/images/programar-introduccion-a-graphql-la-evolucion-del-diseno-de-api-parte-2-921-0.jpg

    2024-05-20

     

    Introducción a GraphQL: la evolución del diseño de API (Parte 2)
    Introducción a GraphQL: la evolución del diseño de API (Parte 2)

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20