Deja paso a los resolvers: un nuevo enfoque para la ejecución de GraphQL

Rate this content
Bookmark
Slides

Aunque GraphQL es declarativo, los resolvers operan campo por campo, capa por capa, lo que a menudo resulta en un trabajo innecesario para la lógica de tu negocio, incluso cuando se utilizan técnicas como DataLoader. En esta charla, Benjie presentará su visión de una nueva estrategia de ejecución de GraphQL de propósito general cuyo enfoque holístico podría conducir a ganancias significativas en eficiencia y escalabilidad para todas las APIs de GraphQL.

16 min
08 Dec, 2022

Video Summary and Transcription

GraphQL ha tenido un gran impacto en la forma en que construimos aplicaciones cliente, sitios web y aplicaciones móviles. A pesar de la dominancia de los resolvers, la especificación de GraphQL no exige su uso. Presentamos Graphast, un nuevo proyecto que compila las operaciones de GraphQL en planes de ejecución y salida, proporcionando optimizaciones avanzadas. En GraphFast, en lugar de resolvers, tenemos plan resolvers que manejan datos futuros. Los plan resolvers de GraphFast son cortos y eficientes, y admiten todas las características de GraphQL moderno.

Available in English

1. Introducción a GraphQL

Short description:

GraphQL ha tenido un gran impacto en la forma en que construimos aplicaciones cliente, sitios web y aplicaciones móviles. Nos permite ser más eficientes, minimizar las idas y vueltas, reducir riesgos, aumentar la productividad y entregar datos en el formato que el cliente espera. Esto facilita y acelera la construcción de cosas que nuestros usuarios aman.

♪ Hola a todos. Mi nombre es Benji y me encanta GraphQL. Creo que GraphQL es increíble. Ha tenido un impacto tan grande en la forma en que construimos nuestras aplicaciones cliente, nuestros sitios web, aplicaciones móviles, y más. Nos ha mostrado cómo podemos ser más eficientes y deshacernos de este sobreconsumo y subconsumo. Realmente hacer las cosas de una manera altamente optimizada para lo que el cliente necesita. Minimizar las idas y vueltas y tener seguridad de tipo reduce los riesgos de que algo salga mal. Manejar resultados parcialmente exitosos significa que incluso si las cosas salen mal, aún podemos mostrar algo útil al usuario. La documentación incorporada aumenta la productividad. Y, por supuesto, al entregar los datos en el formato que el cliente espera, podemos minimizar la cantidad de manipulación que necesitamos hacer en el lado del cliente. Todo esto hace que sea mucho, mucho más fácil y rápido escribir nuestras aplicaciones cliente, lo que facilita y acelera la construcción de cosas que nuestros usuarios aman. ¡GraphQL es increíble! Pero tengo una confesión que hacer.

2. Desafíos con los Resolvers en GraphQL

Short description:

Odio los resolvers. El lenguaje GraphQL es declarativo y, sin embargo, los resolvers convierten la ejecución en un enfoque procedural. Descartan las estrategias de optimización, requieren esfuerzo adicional para la optimización y hacen que los servidores hagan más de lo necesario. A pesar de la dominancia de los resolvers, la especificación de GraphQL no exige su uso. Podemos ejecutar operaciones de cualquier manera siempre y cuando el resultado observable sea el mismo. Los resolvers hacen cumplir la naturaleza de grafo de GraphQL, pero existen formas alternativas de ejecutar operaciones.

Odio los resolvers. Siempre he odiado los resolvers. El lenguaje GraphQL es declarativo, y sin embargo, los resolvers no fueron construidos para aprovechar esta increíble capacidad. En cambio, convierten la ejecución en un enfoque procedural, capa por capa, campo por campo, elemento por elemento. En mi opinión, los resolvers son en gran medida un enfoque de producto mínimo viable para la ejecución. Son simples de entender y especificar, pero dejan problemas como resolver el problema del N más uno en manos del usuario, requiriendo a los diseñadores de esquemas recordar el uso de abstracciones como el Data Loader para lograr un rendimiento aceptable. Y descartan clases enteras de estrategias de optimización.

Si quieres optimizar lo que le estás pidiendo a tu capa de lógica de negocio basado en la consulta GraphQL entrante, por ejemplo, seleccionar solo ciertos campos de una base de datos, o decirle a tu API remota que incluya recursos adicionales necesarios, tienes que lidiar con árboles de sintaxis abstracta o complejidades similares de anticipación o transpilación. Es desagradable y requiere mucho esfuerzo. Incluso las personas que invierten este esfuerzo tienden a hacerlo solo a un nivel bastante superficial, pero las verdaderas ganancias de eficiencia vendrían al llevar esto un poco más lejos. Todo esto significa que GraphQL hace que nuestros servidores hagan más de lo que deberían, consumiendo más ciclos de CPU, realizando más llamadas de red, utilizando más energía, ejerciendo más presión sobre el medio ambiente. Y no está haciendo tanto como podría para ahorrarnos dinero en nuestras facturas de servidor.

De hecho, mi odio por los resolvers es la razón por la que me uní al grupo de trabajo de GraphQL en primer lugar, en 2018. La especificación de GraphQL parece dictar que debemos ejecutar nuestras operaciones utilizando resolvers. Parece tan innecesario. GraphQL, siendo un lenguaje declarativo, ¿por qué debemos estipular que debemos ejecutarlo de manera procedural? A medida que fui aprendiendo la especificación de GraphQL, me di cuenta, por supuesto, de que no estipulamos que debemos usar resolvers en absoluto. Un párrafo justo al comienzo de la especificación de GraphQL, que debo admitir, cuando lo leí por primera vez, lo pasé por alto por completo, fui directamente a la sección de ejecución, establece que los requisitos de conformidad expresados como algoritmos pueden ser cumplidos por una implementación de esta especificación de cualquier manera, siempre y cuando los resultados percibidos se vean equivalentes. Estipulamos que debe parecer que se ejecuta de esa manera pero eso no necesariamente tiene que ser lo que realmente hacemos en el lado del servidor. Mientras el resultado observable sea el mismo, haz lo que quieras. Pero aún tenemos resolvers.

Los resolvers siguen siendo la forma dominante de ejecutar GraphQL. Incluso en proyectos que profundizan un poco más en la optimización de consultas del backend utilizando herramientas como mi módulo de resolución de información de GraphQL, para anticipar y descubrir qué campos están siendo solicitados, todavía estamos usando resolvers. Y la razón detrás de ellos es válida. La forma en que describen la ejecución es correcta. Sin esta definición, GraphQL podría convertirse en más un formato de transferencia que en un motor de ejecución. Los clientes no podrían confiar en las mismas suposiciones, las suposiciones que hacen posible cosas como el almacenamiento en caché normalizado. Porque los resolvers hacen cumplir la naturaleza de grafo de GraphQL, donde nos movemos de nodo a nodo, el valor del nodo en el que estamos no depende de dónde venimos ni de a dónde vamos a continuación. Y sin embargo, esta no tiene que ser la forma en que realmente ejecutamos las operaciones. He estado luchando con este problema una y otra vez durante casi seis años. He realizado muchos experimentos durante ese tiempo.

3. Experimentos y Limitaciones

Short description:

He realizado muchos experimentos durante ese tiempo. Me he sumergido profundamente en la especificación de GraphQL para entender exactamente por qué se especifican los resolvers, de qué nos protegen, por qué se declaran de la manera en que lo hacen. He intentado trabajar dentro de las limitaciones de GraphQLjs para resolver este problema y he tenido cierto éxito.

He realizado muchos experimentos durante ese tiempo. Me he sumergido profundamente en la especificación de GraphQL para entender exactamente por qué se especifican los resolvers, de qué nos protegen, por qué se declaran de la manera en que lo hacen. He intentado trabajar dentro de las limitaciones de GraphQLjs para resolver este problema, y he tenido cierto éxito. Pero siempre me ha molestado que simplemente no se sienta tan ergonómico como debería ser. Las soluciones alternativas que se me han ocurrido han sido realmente torpes y rígidas. Admito que muchas de esas soluciones que se me ocurrieron fueron antes de contar con el apoyo de mis numerosos patrocinadores, por lo que no pude invertir mucho tiempo en su investigación. Pero afortunadamente, mucha gente ha encontrado que el software que escribo es muy útil. Y a medida que ha crecido mi patrocinio, gracias, patrocinadores, el tiempo que puedo invertir en este problema ha aumentado.

4. Introducción a Graphast

Short description:

Hace aproximadamente dos años y medio, me propuse resolver el problema de la planificación de ejecución en GraphQL. Las bases de datos relacionales utilizan SQL, un lenguaje declarativo, para ejecutar consultas de manera eficiente. Aunque GraphQL tiene resolvers, carece de un sistema de planificación de consultas de propósito general. Presentamos Graphast, un nuevo proyecto que compila las operaciones de GraphQL en planes de ejecución y salida, proporcionando optimizaciones avanzadas. El plan de ejecución se construye utilizando pasos que representan datos futuros, similares a los data loaders. El agrupamiento es una característica fundamental de Graphast.

Hace aproximadamente dos años y medio, me propuse, a tiempo parcial, resolver este problema de una vez por todas. Pero antes de entrar en eso, hablemos de mi inspiration.

Aquellos que me conocen saben que me encantan las bases de datos relacionales. Bueno, una base de datos relacional en particular, en realidad. Las bases de datos relacionales utilizan SQL, que es un lenguaje declarativo. Especifica al servidor qué data se requiere, y el servidor decide cómo ejecutarlo. Y puede elegir muchas formas diferentes de ejecutar esa consulta. Por ejemplo, teniendo en cuenta la presencia de índices o determinando cuánta data se espera para saber qué tipo de operaciones intentar realizar. Esto se llama planificación de consultas. Incluso el Postgres moderno tiene cosas como algoritmos genéticos para ayudar a elegir el mejor plan de ejecución, compilación en tiempo real para compilar expresiones a código de máquina para que se puedan ejecutar de manera más eficiente.

Ahora, GraphQL también es declarativo. Así que tenemos resolvers. No tenemos una planificación de ejecución genérica para GraphQL. Pero no es justo decir que no tenemos nada. Muchas personas han sentido este dolor. Ahora tenemos planificadores especializados como el planificador de GraphQL a GraphQL que se encuentra en Apollo Federation, o la optimización de los internos de GraphQL en GraphQL JIT. También tenemos formas de optimizar patrones particulares como proyecciones en Hot Chocolate o transpilación de GraphQL a SQL con Hasura. Pero no tenemos un sistema de planificación de consultas de propósito general que adopte un enfoque holístico y permita optimizaciones advanced sin importar cuál sea su lógica empresarial subyacente, al menos hasta ahora.

Después de esa introducción tan larga, hoy estoy aquí para decir, aparta, resolvers. Hay una nueva forma de ejecutar cualquier operación de GraphQL. El título provisional de este nuevo proyecto es Graphast, y funciona tomando tu operación de GraphQL y compilándola en un plan de ejecución y salida. El plan de salida es un mapeo sencillo de los data que recuperamos durante la fase de ejecución al resultado de GraphQL que queremos mostrar al usuario. Así que pasaremos por alto eso. Principalmente nos interesa el plan de ejecución. Construimos el plan de ejecución siguiendo en su mayoría el algoritmo de ejecución en la especificación de GraphQL. Sin embargo, hacemos esto antes de ejecutarlo realmente. Por lo tanto, no estamos tratando con data concreta como lo haríamos en la especificación. En su lugar, estamos tratando con lo que llamamos pasos que representan estos datos futuros. Cuando llega el momento de ejecutar, un paso es bastante similar a un data loader en el sentido de que acepta una lista de entradas y devuelve una lista de salidas. El agrupamiento está integrado en el corazón mismo de GraphFast.

5. GraphFast Plan Resolvers

Short description:

En GraphFast, en lugar de resolvers, tenemos plan resolvers que manejan datos futuros. Al recorrer los conjuntos de selección y llamar a los plan resolvers, podemos construir un diagrama de plan. El sistema GraphFast realiza la deduplicación, optimización y finalización del plan. La deduplicación simplifica los diagramas de plan, la optimización mejora el rendimiento y la finalización prepara el plan para su ejecución. Por ejemplo, en la API de pagos de Stripe, los pasos pueden comunicarse y pasar información para obtener datos de manera más eficiente.

Mientras que en un esquema tradicional de GraphQL un campo tendría un resolver, en GraphFast tendría un plan resolver. El plan resolver tiene un código similar, como se puede ver a simple vista. Pero en lugar de lidiar con datos concretos en tiempo de ejecución, se ocupa de datos futuros, estos pasos, que representamos con un símbolo de dólar en el código a la derecha.

Al recorrer los conjuntos de selección y llamar a los plan resolvers en cada etapa, podemos construir un diagrama de plan, como este, que expresa lo que debe suceder. Ahora, este diagrama de plan no es lo que se ejecutará finalmente, esto es solo nuestro primer borrador. El sistema GraphFast revisará cada paso en el diagrama de plan y le dará la oportunidad de deduplicarse, optimizarse y finalizarse. Estos son los tres principales métodos del ciclo de vida que estos pasos pueden implementar.

La deduplicación permite que los pasos similares se amalgamen para simplificar nuestros diagramas de plan. La optimización es la fase principal, la más crítica para el rendimiento, y permite que los pasos de la misma familia se comuniquen entre sí y pasen información entre ellos. Por ejemplo, si estamos trabajando con la API de pagos de Stripe, podríamos tener un campo de GraphQL que obtiene el cliente de Stripe. Y luego en nuestra consulta, podríamos tener un campo secundario de ese que obtiene las suscripciones del cliente, y luego varios campos debajo de eso. Cuando se optimiza el paso responsable de obtener las suscripciones, podría determinar que su abuelo está obteniendo datos de un cliente de Stripe y, por lo tanto, decirle al paso del cliente que obtenga las suscripciones al mismo tiempo. Stripe tiene una función llamada expansión para esto. Esto haría que el paso de obtención de suscripciones sea redundante. Entonces, como última acción en Optimizar, puede reemplazarse a sí mismo con un paso que simplemente extraiga los datos relevantes de la respuesta del cliente. De esta manera, ahora solo necesitamos un viaje de ida a Stripe para obtener toda la información que necesitamos.

6. Graphfast Finalize Method and Plan Resolvers

Short description:

El método finalize le da a los pasos la oportunidad de prepararse para la ejecución, como compilar funciones optimizadas o preparar consultas finales de SQL o GraphQL. Los pasos pueden ser implementados por los usuarios y tienen métodos de ciclo de vida opcionales. Graphfast proporciona pasos preconstruidos para preocupaciones comunes y está diseñado para pasar información adicional a la capa de lógica empresarial para una ejecución más eficiente. Los plan resolvers de Graphfast son cortos y eficientes, admitiendo todas las características de GraphQL moderno. Graphfast es compatible con esquemas de GraphQL existentes y se planea su lanzamiento para la primera mitad de 2023 bajo la licencia MIT.

Finalmente, tenemos el método finalize. Normalmente no se utiliza para la mayoría de los pasos, pero le da al paso la oportunidad de prepararse para la ejecución, hacer algo que solo necesita hacerse una vez. Por ejemplo, se puede utilizar para compilar una función optimizada en JavaScript para ejecutar su acción de manera más eficiente. O se puede utilizar para preparar el texto final de la consulta SQL o GraphQL solo una vez.

Los pasos están diseñados para que los implementes tú mismo, al igual que lo harías con un callback de un cargador de datos. Son un poco más poderosos que un cargador de datos y tienen estos métodos de ciclo de vida opcionales que acabamos de discutir. Pero en el caso más simple, todo lo que necesitan es un método execute que tome una lista de valores y devuelva una lista de valores de la misma manera que lo hace un callback de un cargador de datos.

También tenemos una serie de pasos preconstruidos optimizados que puedes usar para manejar preocupaciones comunes, incluyendo uno para cargar en lotes recursos remotos similar a un cargador de datos, o cada uno para mapear listas o acceso para extraer propiedades de objetos. Y estamos desarrollando más pasos optimizados para tratar preocupaciones específicas. Por ejemplo, realizar solicitudes HTTP, enviar consultas GraphQL o comunicarse con bases de datos.

En última instancia, nuestra intención es utilizar estos pasos para pasar información adicional a tu capa de lógica empresarial, sin importar qué sea, para que pueda ejecutar sus tareas de manera más eficiente. Al igual que GraphQL ayuda a eliminar la sobre y la sub-solicitud en el lado del cliente, Graphfast te ayuda a eliminarlo en el lado del servidor. Por ejemplo, si tu lógica empresarial está implementada con un ORM, o algo similar, puedes utilizar esta información adicional para realizar una carga ansiosa selectiva y reducir las consultas a la base de datos. Si tu lógica empresarial proviene de una API HTTP, puedes utilizar esta información contextual para dictar qué parámetros pasar, controlando mejor los datos que estás obteniendo, reduciendo la carga del servidor y el tráfico de red. Y dado que Graphfast está diseñado en torno al concepto de agrupación, nunca más tendrás que preocuparte por el problema de N más uno. Está resuelto para ti, de manera predeterminada, gracias a cómo funciona el sistema.

Al igual que con los resolvers de GraphQL, los plan resolvers de Graphfast están diseñados para ser cortos y solo expresar lo que es necesario para comunicarse entre GraphQL y tu lógica empresarial. Y a pesar de su agradable ergonomía, desbloquean una eficiencia y rendimiento mucho mayores que los resolvers. Graphfast ha sido construido desde cero para admitir todas las características de GraphQL moderno. Consultas, mutaciones, suscripciones, polimorfismo e incluso tecnología de vanguardia como las directivas de stream y defer. Y es compatible con esquemas de GraphQL existentes. Por lo tanto, puedes usar Graphfast para ejecutar solicitudes contra un esquema basado en resolvers existente y luego migrar a plan resolvers campo por campo. Y si ya utilizas Dataloader, migrar a usar Graphfast plan debería ser muy sencillo.

Esperamos lanzar una versión de código abierto de Graphfast bajo la licencia MIT, la misma licencia que utiliza GraphQL.js, en la primera mitad de 2023. Para recibir una notificación cuando estemos listos, ingresa tu dirección de correo electrónico en graphfast.org. Si deseas ayudarme a seguir invirtiendo tiempo en proyectos como este, considera patrocinarme en GitHub sponsors. Incluso podrías obtener acceso anticipado. No dudes en comunicarte conmigo si tienes alguna pregunta. Gracias por tu tiempo y espero que estés tan emocionado acerca del futuro de GraphQL como yo. Gracias. Microsoft Mechanics www.microsoft.com.com

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

GraphQL Galaxy 2021GraphQL Galaxy 2021
32 min
From GraphQL Zero to GraphQL Hero with RedwoodJS
Top Content
We all love GraphQL, but it can be daunting to get a server up and running and keep your code organized, maintainable, and testable over the long term. No more! Come watch as I go from an empty directory to a fully fledged GraphQL API in minutes flat. Plus, see how easy it is to use and create directives to clean up your code even more. You're gonna love GraphQL even more once you make things Redwood Easy!
Vue.js London Live 2021Vue.js London Live 2021
24 min
Local State and Server Cache: Finding a Balance
Top Content
How many times did you implement the same flow in your application: check, if data is already fetched from the server, if yes - render the data, if not - fetch this data and then render it? I think I've done it more than ten times myself and I've seen the question about this flow more than fifty times. Unfortunately, our go-to state management library, Vuex, doesn't provide any solution for this.For GraphQL-based application, there was an alternative to use Apollo client that provided tools for working with the cache. But what if you use REST? Luckily, now we have a Vue alternative to a react-query library that provides a nice solution for working with server cache. In this talk, I will explain the distinction between local application state and local server cache and do some live coding to show how to work with the latter.
React Day Berlin 2022React Day Berlin 2022
29 min
Get rid of your API schemas with tRPC
Do you know we can replace API schemas with a lightweight and type-safe library? With tRPC you can easily replace GraphQL or REST with inferred shapes without schemas or code generation. In this talk we will understand the benefit of tRPC and how apply it in a NextJs application. If you want reduce your project complexity you can't miss this talk.

Workshops on related topic

GraphQL Galaxy 2021GraphQL Galaxy 2021
140 min
Build with SvelteKit and GraphQL
Top Content
Featured WorkshopFree
Have you ever thought about building something that doesn't require a lot of boilerplate with a tiny bundle size? In this workshop, Scott Spence will go from hello world to covering routing and using endpoints in SvelteKit. You'll set up a backend GraphQL API then use GraphQL queries with SvelteKit to display the GraphQL API data. You'll build a fast secure project that uses SvelteKit's features, then deploy it as a fully static site. This course is for the Svelte curious who haven't had extensive experience with SvelteKit and want a deeper understanding of how to use it in practical applications.

Table of contents:
- Kick-off and Svelte introduction
- Initialise frontend project
- Tour of the SvelteKit skeleton project
- Configure backend project
- Query Data with GraphQL
- Fetching data to the frontend with GraphQL
- Styling
- Svelte directives
- Routing in SvelteKit
- Endpoints in SvelteKit
- Deploying to Netlify
- Navigation
- Mutations in GraphCMS
- Sending GraphQL Mutations via SvelteKit
- Q&A
React Advanced Conference 2022React Advanced Conference 2022
95 min
End-To-End Type Safety with React, GraphQL & Prisma
Featured WorkshopFree
In this workshop, you will get a first-hand look at what end-to-end type safety is and why it is important. To accomplish this, you’ll be building a GraphQL API using modern, relevant tools which will be consumed by a React client.
Prerequisites: - Node.js installed on your machine (12.2.X / 14.X)- It is recommended (but not required) to use VS Code for the practical tasks- An IDE installed (VSCode recommended)- (Good to have)*A basic understanding of Node.js, React, and TypeScript
GraphQL Galaxy 2022GraphQL Galaxy 2022
112 min
GraphQL for React Developers
Featured Workshop
There are many advantages to using GraphQL as a datasource for frontend development, compared to REST APIs. We developers in example need to write a lot of imperative code to retrieve data to display in our applications and handle state. With GraphQL you cannot only decrease the amount of code needed around data fetching and state-management you'll also get increased flexibility, better performance and most of all an improved developer experience. In this workshop you'll learn how GraphQL can improve your work as a frontend developer and how to handle GraphQL in your frontend React application.
React Summit 2022React Summit 2022
173 min
Build a Headless WordPress App with Next.js and WPGraphQL
Top Content
WorkshopFree
In this workshop, you’ll learn how to build a Next.js app that uses Apollo Client to fetch data from a headless WordPress backend and use it to render the pages of your app. You’ll learn when you should consider a headless WordPress architecture, how to turn a WordPress backend into a GraphQL server, how to compose queries using the GraphiQL IDE, how to colocate GraphQL fragments with your components, and more.
GraphQL Galaxy 2020GraphQL Galaxy 2020
106 min
Relational Database Modeling for GraphQL
Top Content
WorkshopFree
In this workshop we'll dig deeper into data modeling. We'll start with a discussion about various database types and how they map to GraphQL. Once that groundwork is laid out, the focus will shift to specific types of databases and how to build data models that work best for GraphQL within various scenarios.
Table of contentsPart 1 - Hour 1      a. Relational Database Data Modeling      b. Comparing Relational and NoSQL Databases      c. GraphQL with the Database in mindPart 2 - Hour 2      a. Designing Relational Data Models      b. Relationship, Building MultijoinsTables      c. GraphQL & Relational Data Modeling Query Complexities
Prerequisites      a. Data modeling tool. The trainer will be using dbdiagram      b. Postgres, albeit no need to install this locally, as I'll be using a Postgres Dicker image, from Docker Hub for all examples      c. Hasura
GraphQL Galaxy 2021GraphQL Galaxy 2021
48 min
Building GraphQL APIs on top of Ethereum with The Graph
WorkshopFree
The Graph is an indexing protocol for querying networks like Ethereum, IPFS, and other blockchains. Anyone can build and publish open APIs, called subgraphs, making data easily accessible.

In this workshop you’ll learn how to build a subgraph that indexes NFT blockchain data from the Foundation smart contract. We’ll deploy the API, and learn how to perform queries to retrieve data using various types of data access patterns, implementing filters and sorting.

By the end of the workshop, you should understand how to build and deploy performant APIs to The Graph to index data from any smart contract deployed to Ethereum.