Comment nous avons utilisé GraphQL en développement sur l'exemple du catalogue Internet

Dans cet article, nous partageons notre expérience de l'utilisation réelle de GraphQL pour créer un catalogue en ligne. Parlons des avantages et des inconvénients de ce langage de requête que nous avons trouvé lors de l'utilisation de la pile GraphQL + Apollo Client, Next JS (SSR) + TypeScript.



Avertissement: nous ne décrivons pas en détail «l'essence magique» et l'histoire de l'origine de l'instrument (on en a déjà beaucoup parlé). Nous espérons que ceux qui connaissent déjà GraphQL bénéficieront de notre expérience pratique.







Nous avons récemment participé à la création d'un catalogue Internet de matériel d'éclairage. Pour créer des pages publicitaires, les administrateurs du site pourraient utiliser le concepteur: sélectionnez les blocs nécessaires (par exemple, des bannières ou des listes), remplissez-les de données, définissez l'ordre d'affichage et d'autres paramètres. Dans le même temps, l'application a rendu des composants qui étaient prédisposés pour chaque type de bloc.



Dans chaque catégorie du catalogue en ligne, des fiches de différents groupes de produits étaient affichées et, lorsque vous survolez la fiche, une liste des propriétés des produits associés s'affiche.



Nous devions afficher les propriétés des marchandises dans une structure arborescente et offrir une vitesse de traitement des demandes suffisamment élevée.



Nous avons choisi l'ordre de travail suivant avec les demandes:



  1. Demandez tous les groupes de produits pour une catégorie spécifique (généralement environ 50 groupes).
  2. Demandez une liste de produits pour chaque groupe.
  3. Demandez une liste de propriétés pour chaque produit.


Puisque nous développions une application basée sur GraphQL, nous étions prêts pour le fait que certaines des données auraient une structure imbriquée assez complexe. Bien que le branchement de cette structure soit logique pour le développement backend, une logique "supplémentaire" a dû être écrite sur le front pour traiter les données et les sortir au composant, selon la conception.



En raison des particularités du constructeur GraphQL, nous avons décidé de collecter les propriétés et les valeurs uniques non pas à l'arrière, mais à l'avant, puis à les rendre dans un ordre spécifique. Cependant, le traitement de la demande était trop lent - jusqu'à 20 secondes, ce qui, bien sûr, ne nous convenait pas.



Pour cette raison, nous avons commencé à diviser chaque requête en petites sous-requêtes et à charger les données par portions. En conséquence, l'application a sensiblement amélioré sa vitesse - les demandes ne prenaient pas plus de 2 secondes. Bien que le nombre de demandes ait augmenté, la charge sur le système a diminué et la nécessité de charger les données inutilisées a disparu.



Ensuite, parlons plus en détail directement de l'utilisation de GraphQL.



Fonctionnalités de travail avec GraphQL



Les exigences du produit étaient pour nous d'utiliser le langage de requête GraphQL développé par Facebook. Pour cette raison, nous ne nous sommes pas livrés à des arguments sans fin pour savoir lequel est le meilleur, GraphQL ou REST - au lieu de cela, nous avons décidé d'utiliser la bonne technologie de la manière la plus efficace, en tenant compte de toutes ses forces.



Nous avons pris en compte le fait que GraphQL a été conçu pour simplifier le développement et la maintenance des API, principalement en ayant un seul point de terminaison.



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


GraphQL a un seul point de terminaison. Cela signifie que nous n'avons pas besoin de faire deux requêtes distinctes pour obtenir des données à partir de deux ressources différentes. GraphQL consolide toutes les demandes et mutations en un seul point de terminaison et le rend disponible pour référence, tout en évitant le contrôle de version inhérent aux API REST.



GraphQL offre la possibilité d'optimiser les performances et d'obtenir exactement les données nécessaires pour le moment à l'aide d'une syntaxe de requête spéciale: les champs obligatoires doivent être répertoriés dans la requête.



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


GraphQL utilise des entités fortement typées et un schéma de type, ce qui à son tour est utile en conjonction avec TypeScript et la génération de type front-end.



Beaucoup de ces fonctionnalités peuvent certainement être implémentées sur les API REST, cependant, GraphQL les fournit prêtes à l'emploi.



Pour l'interaction client avec GraphQL, nous avons choisi la solution la plus populaire avec une bonne documentation - la bibliothèque Apollo Client, qui vous permet d'obtenir, de mettre en cache et de modifier les données d'application. Apollo Client vous donne la possibilité d'utiliser des crochets et des outils de demande et de mutation pour suivre facilement l'état de téléchargement / d'erreur.



Sur le front également, nous avons utilisé le framework NextJS, choisi en tenant compte des facteurs suivants: pré-rendu (NextJS fournit un mécanisme très simple pour implémenter la génération statique et SSR prêts à l'emploi), prise en charge de toutes les solutions css-in-js existantes, routage dynamique, prise en charge des fichiers statiques (par exemple, des images) dans les composants React.



Enfin, lorsque les technologies sont sélectionnées, passons au développement. À première vue, tout semble bon: bibliothèques modernes, bonne documentation, nombreux cas d'utilisation différents. Chacune des technologies est conçue individuellement pour faciliter un développement confortable et rapide. En créant un passe-partout, indispensable, et en concevant des composants d'interface utilisateur, nous avons progressivement abordé le stade de l'interaction efficace entre nos bibliothèques. Ici, tout le plaisir a commencé.



En regardant plus en profondeur les mécanismes de NextJS, nous pouvons voir qu'il utilise deux formes de pré-rendu: la génération statique et SSR. Ces deux stratégies sont implémentées à l'aide de fonctions spéciales de préchargement de données:



`getInitialProps` (SSR) - lors du premier chargement, il s'exécute sur le serveur, demande des données puis les transmet au composant en tant qu'éléments.



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


`getStaticProps` (Static Generation) - s'exécute au stade de la construction. NextJS pré-rend la page en utilisant les accessoires retournés par cette fonction.



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


`getServerSideProps` (Rendu côté serveur) - NextJS pré-rend la page à chaque demande, en utilisant les données renvoyées par cette fonction comme accessoires.



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


Ainsi, toutes les fonctions listées sont déclarées en dehors du composant, reçoivent des données et les transmettent au composant. Cela conduit à l'un des problèmes d'interaction avec le client Apollo. La bibliothèque fournit des mécanismes de requête tels que le hook ʻuseQuery` et le composant `Query`, et aucune des méthodes proposées ne peut être utilisée en dehors du composant. Dans cet esprit, dans notre projet, nous avons décidé d'utiliser la bibliothèque next-with-apollo et nous avons finalement été satisfaits du résultat.



Un autre problème connu avec Apollo Client, que nous avons également rencontré, est la possibilité d'une boucle infinie de requêtes depuis le hook ʻuseQuery`. Le nœud du problème est que le client Apollo continue d'envoyer des demandes indéfiniment jusqu'à ce qu'il reçoive correctement les données. Cela peut conduire à une situation où le composant «se bloque» dans une requête sans fin, si le serveur ne peut pas renvoyer de données pour une raison quelconque.



Dans notre cas, l'une des raisons était le changement de schéma au verso. Apollo génère lui-même les types, donc lors de l'écriture de requêtes sur le front, nous devions suivre les types générés pour éviter les bogues. Par exemple, nous avons eu une demande de travail, sans aucun problème de type. Cependant, lors de la modification du schéma sur le backend, les types changeaient en même temps, ce qui pouvait entraîner l'arrêt du fonctionnement de la requête de travail. Compte tenu de cela, il est optimal de mettre en œuvre immédiatement la journalisation et un système clair de gestion des erreurs afin de sauver les nerfs et le temps de l'équipe.



À notre avis, il s'est avéré très utile que dans les requêtes graphql, vous puissiez spécifier exactement quelles données doivent être reçues. Lors de l'envoi d'un grand nombre de requêtes, cela évite de traiter des données inutiles. À son tour, cela peut avoir un impact significatif sur les performances à mesure que la quantité de données augmente.



Il est à noter que l'un des avantages de GraphQL est considéré comme la commodité du développement et du support API, mais cette propriété est plus importante pour le développement backend et, selon nos observations, n'a pas d'impact significatif sur le front. En raison de la génération des types, chaque fois que le schéma a été modifié sur le fond de panier, nous réécrivons toutes les requêtes concernées. Beck devait également affiner le système si nous devions obtenir quelque chose sur le front qui n'avait pas encore été mis en œuvre. Dans le même temps, la génération de types lors de l'utilisation de TypeScript a permis de détecter de nombreuses erreurs au stade de l'écriture du code.



Résumer



D'après nos observations, GraphQL est largement utilisé dans divers types de solutions informatiques et apporte certains avantages à l'équipe de développement. Résumons les principales fonctionnalités de GraphQL que nous avons rencontrées lors du développement du projet:



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


! , .



All Articles