Lorsqu'il s'agit de concevoir l'API du système, les ingénieurs logiciels envisagent souvent différentes options telles que REST vs RPC vs GraphQL (ou d'autres approches hybrides) pour déterminer la meilleure solution pour une tâche ou un projet spécifique.
Dans cet article, nous explorons comment l'API de la chronologie de la maison X (Twitter) (x.com/home) est conçue et quelles approches elle utilise pour résoudre les défis suivants :
Nous explorerons ces défis uniquement au niveau de l'API, en traitant l'implémentation du backend comme une boîte noire, puisque nous n'avons pas accès au code du backend lui-même.
Afficher ici les requêtes et réponses exactes peut être fastidieux et difficile à suivre car les objets profondément imbriqués et répétitifs sont difficiles à lire. Pour faciliter la visualisation de la structure de charge utile requête/réponse, j'ai tenté de "taper" l'API de la chronologie d'accueil dans TypeScript. Ainsi, en ce qui concerne les exemples de requête/réponse, j'utiliserai les types de requête et de réponse au lieu des objets JSON réels. N'oubliez pas non plus que les types sont simplifiés et que de nombreuses propriétés sont omises par souci de concision.
Vous pouvez trouver tous les types dans le fichier types/x.ts ou au bas de cet article dans la section « Annexe : Tous les types au même endroit ».
La récupération de la liste des tweets pour la chronologie d'accueil commence par lePOSTEdemande au point de terminaison suivant :
POST https://x.com/i/api/graphql/{query-id}/HomeTimelineVoici un type de corps de requête simplifié :
tapez TimelineRequest = { queryId : string ; // Variables 's6ERr1UxkxxBx4YundNsXw' : { nombre : nombre ; // 20 curseur ? : chaîne ; // 'DAAACgGBGedb3Vx__9sKAAIZ5g4QENc99AcAAwAAIAIAAA' vuTweetIds : string[]; // ['1867041249938530657', '1867041249938530659'] }; fonctionnalités : fonctionnalités ; } ; tapez Features = { articles_preview_enabled: boolean; view_counts_everywhere_api_enabled : booléen ; //... }Et voici un type de corps de réponse simplifié (nous approfondirons les sous-types de réponse ci-dessous) :
tapez TimelineResponse = { data : { home : { home_timeline_urt : { instructions : (TimelineAddEntries | TimelineTerminateTimeline)[] ; objets de réponse : { feedbackActions : TimelineAction[] ; } ; } ; } ; } ; } ; tapez TimelineAddEntries = { tapez : 'TimelineAddEntries'; entrées : (TimelineItem | TimelineCursor | TimelineModule)[] ; } ; tapez TimelineItem = {entryId: string; // 'tweet-1867041249938530657' sortIndex : chaîne ; // Contenu '1866561576636152411' : { __typename : 'TimelineTimelineItem'; itemContent : TimelineTweet ; feedbackInfo : { feedbackKeys : ActionKey[] ; // ['-1378668161'] }; } ; } ; tapez TimelineTweet = { __typename : 'TimelineTweet' ; tweet_results : { résultat : Tweet ; } ; } ; tapez TimelineCursor = {entryId: string; // 'curseur-haut-1867041249938530657' sortIndex : chaîne ; // Contenu '1866961576813152212' : { __typename : 'TimelineTimelineCursor'; valeur : chaîne ; // 'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA' curseurType : 'Haut' | 'Bas'; } ; } ; tapez ActionKey = chaîne ;Il est intéressant de noter ici que « l'obtention » des données se fait via le « POSTing », ce qui n'est pas courant pour l'API de type REST mais c'est courant pour une API de type GraphQL. Aussi, legraphqlune partie de l'URL indique que X utilise la version GraphQL pour son API.
J'utilise le mot « saveur » ici parce que le corps de la requête lui-même ne ressemble pas à une pure requête GraphQL, où nous pouvons décrire la structure de réponse requise, répertoriant toutes les propriétés que nous voulons récupérer :
# Un exemple de structure de requête GraphQL pure qui n'est *pas* utilisée dans l'API X. { tweets { identifiant description créé_à média { type url # ... } auteur { identifiant nom # ... } # ... } }L'hypothèse ici est que l'API de la chronologie d'accueil n'est pas une pure API GraphQL, mais un mélange de plusieurs approches. Passer les paramètres dans une requête POST comme celle-ci semble plus proche de l'appel RPC "fonctionnel". Mais en même temps, il semble que les fonctionnalités de GraphQL pourraient être utilisées quelque part sur le backend derrière le gestionnaire/contrôleur de point de terminaison HomeTimeline. Un tel mélange peut également être dû à un code hérité ou à une sorte de migration en cours. Mais encore une fois, ce ne sont que mes spéculations.
Vous remarquerez peut-être également que le mêmeTimelineRequest.queryIdest utilisé dans l'URL de l'API ainsi que dans le corps de la requête API. Ce queryId est très probablement généré sur le backend, puis il est intégré dans lemain...
[Courte citation de 8% de l'article original]