Cómo usamos GraphQL en el desarrollo en el ejemplo del catálogo de Internet

En este artículo, compartimos nuestra experiencia de uso de GraphQL en el mundo real para crear un catálogo en línea. Hablemos de las ventajas y desventajas de este lenguaje de consulta que encontramos al usar GraphQL + Apollo Client, Next JS (SSR) + pila TypeScript.



Aviso legal: no describimos en detalle la "esencia mágica" y la historia del origen del instrumento (ya se ha dicho mucho sobre esto). Esperamos que aquellos que ya están familiarizados con GraphQL se beneficien de nuestra experiencia práctica.







Recientemente participamos en la creación de un catálogo de Internet de equipos de iluminación. Para crear páginas publicitarias, los administradores del sitio pueden usar el diseñador: seleccionar los bloques necesarios (por ejemplo, banners o listas), llenarlos con datos, definir el orden de visualización y otras configuraciones. Al mismo tiempo, la aplicación procesó los componentes que se preestablecieron para cada tipo de bloque.



En cada categoría del catálogo en línea, se mostraban tarjetas de varios grupos de productos y, cuando pasaba el mouse sobre la tarjeta, se mostraba una lista de propiedades de los productos adjuntos.



Necesitábamos mostrar las propiedades de los productos en una estructura de árbol y proporcionar una velocidad suficientemente alta de procesamiento de solicitudes.



Hemos elegido el siguiente orden de trabajo con las solicitudes:



  1. Solicite todos los grupos de productos para una categoría específica (generalmente alrededor de 50 grupos).
  2. Solicite una lista de productos para cada grupo.
  3. Solicite un listado de propiedades para cada producto.


Como estábamos desarrollando una aplicación basada en GraphQL, estábamos preparados para el hecho de que algunos de los datos tendrían una estructura anidada bastante compleja. Aunque ramificar esta estructura era lógico para el desarrollo de backend, era necesario escribir alguna lógica "adicional" en el frente para procesar los datos y enviarlos al componente, de acuerdo con el diseño.



Debido a las peculiaridades del constructor GraphQL, decidimos recopilar propiedades y valores únicos no en la parte posterior, sino en el frente, y luego renderizarlos en un orden específico. Sin embargo, el procesamiento de la solicitud fue demasiado lento, hasta 20 segundos, lo que, por supuesto, no nos convenía.



Por esta razón, comenzamos a dividir cada solicitud en pequeñas subconsultas y a cargar datos en porciones. Como resultado, la aplicación mejoró notablemente en velocidad: las solicitudes no tardaron más de 2 segundos. Aunque el número de solicitudes ha aumentado, la carga en el sistema ha disminuido y ha desaparecido la necesidad de cargar datos no utilizados.



A continuación, hablemos con más detalle directamente sobre cómo trabajar con GraphQL.



Características de trabajar con GraphQL



Los requisitos del producto eran que usáramos el lenguaje de consulta GraphQL desarrollado por Facebook. Por esta razón, no nos permitimos discusiones interminables sobre cuál es mejor, GraphQL o REST, sino que decidimos utilizar la tecnología adecuada de la manera más eficiente, teniendo en cuenta todas sus fortalezas.



Tomamos en cuenta que GraphQL fue diseñado para simplificar el desarrollo y mantenimiento de API, principalmente al tener un solo punto final.



GET	/news
GET	/posts
POST	/news
POST	/post


GraphQL tiene un único punto final. Esto significa que no necesitamos realizar dos solicitudes independientes para obtener datos de dos recursos diferentes. GraphQL consolida todas las solicitudes y mutaciones en un punto final y lo pone a disposición para referencia, y también le permite alejarse del control de versiones inherente a las API REST.



GraphQL proporciona la capacidad de optimizar el rendimiento y obtener exactamente los datos que se necesitan en este momento utilizando una sintaxis de consulta especial: los campos obligatorios deben estar listados en la consulta.



const FETCH_USER_DATA = gql`
 query FetchUserData {
   user {
     firstName
     lastName
     date
   }
 }
`;


GraphQL usa entidades fuertemente tipadas y esquema de tipo, que a su vez es útil junto con TypeScript y la generación de tipos en el frente.



Muchas de estas características ciertamente se pueden implementar en las API REST, sin embargo, GraphQL las proporciona listas para usar.



Para la interacción del cliente con GraphQL, elegimos la solución más popular con buena documentación: la biblioteca Apollo Client, que le permite obtener, almacenar en caché y modificar los datos de la aplicación. Apollo Client le brinda la capacidad de usar herramientas y ganchos de solicitud y mutación para rastrear fácilmente el estado de descarga / error.



También en el frente, usamos el marco NextJS, elegido teniendo en cuenta los siguientes factores: pre-renderizado (NextJS proporciona un mecanismo muy simple para implementar la generación estática y SSR fuera de la caja), soporte para todas las soluciones existentes de css-in-js, enrutamiento dinámico, soporte para archivos estáticos (por ejemplo, imágenes) en los componentes de React.



Finalmente, cuando se seleccionen las tecnologías, pasemos al desarrollo. A primera vista, todo se ve bien: bibliotecas modernas, buena documentación, muchos casos de uso diferentes. Cada una de las tecnologías está diseñada individualmente para facilitar un desarrollo cómodo y rápido. Al crear un modelo estándar, que era indispensable, y diseñar componentes de interfaz de usuario, nos acercamos gradualmente a la etapa de interacción efectiva entre nuestras bibliotecas. Aquí empezó toda la diversión.



Al profundizar en los mecanismos de NextJS, podemos ver que utiliza dos formas de pre-renderizador: generación estática y SSR. Ambas estrategias se implementan utilizando funciones especiales de precarga de datos:



`getInitialProps` (SSR): cuando se carga por primera vez, se ejecuta en el servidor, solicita datos y luego los pasa al componente como accesorios.



function Component ({data}) {
 ...
}
 
Component.getInitialProps = async (ctx) => {
 const res = await fetch('https://...')
 const json = await res.json()
 return { data: json.data }
}


`getStaticProps` (Generación estática): se ejecuta en la etapa de compilación. NextJS procesa previamente la página utilizando los accesorios devueltos por esta función.



export async function getStaticProps(context) {
 return {
   props: {}, //       props
 }
}


`getServerSideProps` (Representación del lado del servidor): NextJS procesa previamente la página en cada solicitud, utilizando los datos devueltos por esta función como accesorios.



export async function getServerSideProps(context) {
 return {
   props: {}, //       props
 }
}


Así, todas las funciones listadas se declaran fuera del componente, reciben algunos datos y los pasan al componente. Esto conduce a uno de los problemas de interacción con Apollo Client. La biblioteca proporciona mecanismos de consulta como el gancho ʻuseQuery` y el componente `Query`, y ninguno de los métodos propuestos puede usarse fuera del componente. Con esto en mente, en nuestro proyecto, decidimos usar la biblioteca next-with-apollo y al final quedamos satisfechos con el resultado.



Otro problema conocido con Apollo Client, que también encontramos, es la posibilidad de un ciclo infinito de solicitudes del gancho ʻuseQuery`. El meollo del problema es que Apollo Client continúa enviando solicitudes de manera indefinida hasta que recibe datos con éxito. Esto puede llevar a una situación en la que el componente se "cuelgue" en una solicitud infinita, si el servidor no puede devolver datos por alguna razón.



En nuestro caso, una de las razones fue el cambio de esquema en la parte posterior. Apollo genera los tipos por sí solo, por lo que al escribir consultas en el frente, tuvimos que seguir los tipos generados para evitar errores. Por ejemplo, teníamos una solicitud de trabajo, sin problemas de tipo. Sin embargo, al cambiar el esquema en el backplane, los tipos cambiaron al mismo tiempo, por lo que la solicitud de trabajo podría dejar de funcionar. Dado esto, es óptimo implementar de inmediato el registro y un sistema de manejo de errores claro para ahorrar tiempo y nervios al equipo.



En nuestra opinión, resultó bastante útil que en las consultas de graphql se pueda especificar exactamente qué datos se deben recibir. Al enviar una gran cantidad de solicitudes, esto evita procesar datos innecesarios. A su vez, esto puede afectar significativamente el rendimiento a medida que aumenta la cantidad de datos.



Vale la pena señalar que una de las ventajas de GraphQL se considera la conveniencia del desarrollo y el soporte de API, pero esta propiedad es más significativa para el desarrollo de backend y, según nuestras observaciones, no tiene un impacto significativo en el frente. Debido a la generación de tipos, cada vez que se cambiaba el esquema en el backplane, reescribíamos todas las consultas afectadas. Beck también tuvo que refinar el esquema si necesitábamos obtener algo en el frente que aún no se había implementado. Al mismo tiempo, la generación de tipos al usar TypeScript hizo posible detectar muchos errores en la etapa de escritura del código.



Resumiendo



Según nuestras observaciones, GraphQL se usa ampliamente en varios tipos de soluciones de TI y brinda ciertas ventajas para el equipo de desarrollo. Resumamos las principales características de GraphQL que encontramos durante el desarrollo del proyecto:



  • . graphql , TypeScript .
  • . GraphQL - . , ( , ). graphql «» ,
  • . graphql- , . .
  • . GraphQL , .


! , .



All Articles