1(self.webpackChunktrekhleb_github_io=self.webpackChunktrekhleb_github_io||[]).push([[5336],{3150:function(n,s,a){var e=a(9720).w_;n.exports.C=function(n){return e({tag:"svg",attr:{viewBox:"0 0 24 24"},child:[{tag:"path",attr:{d:"M11.953,2C6.465,2,2,6.486,2,12s4.486,10,10,10s10-4.486,10-10S17.493,2,11.953,2z M12,20c-4.411,0-8-3.589-8-8 s3.567-8,7.953-8C16.391,4,20,7.589,20,12S16.411,20,12,20z"}},{tag:"path",attr:{d:"M11 7H13V14H11zM11 15H13V17H11z"}}]})(n)}},9881:function(n,s,a){var e=a(9720).w_;n.exports.L=function(n){return e({tag:"svg",attr:{viewBox:"0 0 16 16",fill:"currentColor"},child:[{tag:"path",attr:{fillRule:"evenodd",d:"M8 16A8 8 0 108 0a8 8 0 000 16zm.93-9.412l-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM8 5.5a1 1 0 100-2 1 1 0 000 2z",clipRule:"evenodd"}}]})(n)}},4363:function(n,s,a){var e=a(9720).w_;n.exports.v=function(n){return e({tag:"svg",attr:{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},child:[{tag:"rect",attr:{x:"3",y:"4",width:"18",height:"18",rx:"2",ry:"2"}},{tag:"line",attr:{x1:"16",y1:"2",x2:"16",y2:"6"}},{tag:"line",attr:{x1:"8",y1:"2",x2:"8",y2:"6"}},{tag:"line",attr:{x1:"3",y1:"10",x2:"21",y2:"10"}}]})(n)}},550:function(n,s,a){var e=a(9720).w_;n.exports.T=function(n){return e({tag:"svg",attr:{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},child:[{tag:"circle",attr:{cx:"12",cy:"12",r:"10"}},{tag:"polyline",attr:{points:"12 6 12 12 16 14"}}]})(n)}},898:function(n,s,a){"use strict";a.r(s),a.d(s,{default:function(){return i}});var e=a(1151),t=a(7294);function o(n){const s=Object.assign({span:"span",p:"p",a:"a",strong:"strong",ul:"ul",li:"li",blockquote:"blockquote",h2:"h2",h3:"h3",ol:"ol",em:"em"},(0,e.ah)(),n.components);return t.createElement(t.Fragment,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<span\n class="gatsby-resp-image-wrapper"\n style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; "\n >
1\n <span\n class="gatsby-resp-image-background-image"\n style="padding-bottom: 40.8%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABGklEQVR42o1SsaqDQBDUqEEkhagohDQ2BiWF1imNWIrGKmhjpX6CjYWdjYXE//F3/IbAPO7AwONF8opjb3e52ZnZYxiGwafDsiwMw4BpmuB5ntY4joOiKLS32+1o/PD2L9gKUNc1ns8nbrcbzYMgwP1+fw/cIPO7sN/vIYoiLMuCrut4PB6oqgpxHCPPcxwOB7iuiyiKcD6ftwGJHBLTNMU8z1iWBZqm4XK5IMsyjOMIVVWpBX3fo2ka+L6/DUg8IdFxHLxeL0zTRHNZljEMA2UXhiFOpxNlS8AI06+SBUHA8XikHhH5xEfP81AUBcqyfMuUJIn2/7WU1fAkSXC9Xundtm10XYe2bbeW8R2QLGC1gtSIh+u32QL8AVgL0gS+Dxv8AAAAAElFTkSuQmCC\'); background-size: cover; display: block;"\n ></span>\n <img\n class="gatsby-resp-image-image"\n alt="X API Design"\n title=""\n src="/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/00d43/01-cover.png"\n srcset="/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/63868/01-cover.png 250w,\n/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/0b533/01-cover.png 500w,\n/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/00d43/01-cover.png 1000w,\n/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/aa440/01-cover.png 1500w,\n/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/e8950/01-cover.png 2000w,\n/static/6c3fe261d3b0a2dfbaa7d2e03447f6d6/c4093/01-cover.png 2646w"\n sizes="(max-width: 1000px) 100vw, 1000px"\n style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n loading="lazy"\n decoding="async"\n />\n </span>'}}),"\n",t.createElement(s.p,null,"When it comes to designing the API of the system the software engineers often consider different options like ",t.createElement(s.a,{href:"https://okso.app/showcase/system-design/page/0d03d895-b5b1-40c6-3549-945df9d98dcd"},"REST vs RPC vs GraphQL")," (or other hybrid approaches) to determine the best fit for a specific task or project."),"\n",t.createElement(s.p,null,"In this article, we explore how the ",t.createElement(s.strong,null,"X")," (",t.createElement(s.strong,null,"Twitter"),") home timeline (x.com/home) API is designed and what approaches they use to solve the following challenges:"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"How to fetch the list of tweets"),"\n",t.createElement(s.li,null,"How to do a sorting and pagination"),"\n",t.createElement(s.li,null,"How to return the hierarchical/linked entities (tweets, users, media)"),"\n",t.createElement(s.li,null,"How to get tweet details"),"\n",t.createElement(s.li,null,'How to "like" a tweet'),"\n"),"\n",t.createElement(s.p,null,"We will only explore these challenges on the API level, treating the backend implementation as a black box, since we don't have access to the backend code itself."),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<span\n class="gatsby-resp-image-wrapper"\n style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; "\n >
1\n <span\n class="gatsby-resp-image-background-image"\n style="padding-bottom: 61.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAexGkzWD/8QAGhAAAgIDAAAAAAAAAAAAAAAAAgMAEgEQMf/aAAgBAQABBQJhUBTsN0fFkVp//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxABAAICAwAAAAAAAAAAAAAAAQAQAhEiQXH/2gAIAQEABj8CcnqcRPaWO2v/xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAhMf/aAAgBAQABPyEzzBeZeZh0sm5Eo5nZJ5DW7//aAAwDAQACAAMAAAAQuA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhQTFhcYGh/9oACAEBAAE/EF0TINr4m8AyDVXw9kQAVtMCDkAbsPTj5DRCwQVnoILJn//Z\'); background-size: cover; display: block;"\n ></span>\n <img\n class="gatsby-resp-image-image"\n alt="X home timeline Example"\n title=""\n src="/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/a2510/02-home.jpg"\n srcset="/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/0479a/02-home.jpg 250w,\n/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/41099/02-home.jpg 500w,\n/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/a2510/02-home.jpg 1000w,\n/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/c58a3/02-home.jpg 1500w,\n/static/cb6b75eb6eb338b77c7fa05cc9bb7c06/3acf0/02-home.jpg 2000w"\n sizes="(max-width: 1000px) 100vw, 1000px"\n style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n loading="lazy"\n decoding="async"\n />\n </span>'}}),"\n",t.createElement(s.blockquote,null,"\n",t.createElement(s.p,null,"Showing the exact requests and responses here might be cumbersome and hard to follow since the deeply nested and repetitive objects are hard to read. To make it easier to see the request/response payload structure, I've made my attempt to \"type out\" the home timeline API in TypeScript. So when it comes to the request/response examples I'll use the request and response ",t.createElement(s.strong,null,"types")," instead of actual JSON objects. Also, remember that the types are simplified and many properties are omitted for brevity."),"\n",t.createElement(s.p,null,"You may find all types in ",t.createElement(s.a,{href:"https://github.com/trekhleb/trekhleb.github.io/blob/master/src/posts/2024/api-design-x-home-timeline/types/x.ts"},"types/x.ts"),' file or at the bottom of this article in the "Appendix: All types in one place" section.'),"\n"),"\n",t.createElement(s.h2,{id:"fetching-the-list-of-tweets",style:{position:"relative"}},"Fetching the list of tweets",t.createElement(s.a,{href:"#fetching-the-list-of-tweets","aria-label":"fetching the list of tweets permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.h3,{id:"the-endpoint-and-requestresponse-structure",style:{position:"relative"}},"The endpoint and request/response structure",t.createElement(s.a,{href:"#the-endpoint-and-requestresponse-structure","aria-label":"the endpoint and requestresponse structure permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"Fetching the list of tweets for the home timeline starts with the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}})," request to the following endpoint:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">POST https://x.com/i/api/graphql/{query-id}/HomeTimeline</code></pre></div>'}}),"\n",t.createElement(s.p,null,"Here is a simplified ",t.createElement(s.strong,null,"request")," body type:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineRequest</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n queryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'s6ERr1UxkxxBx4YundNsXw\'</span>\n variables<span class="token operator">:</span> <span class="token punctuation">{</span>\n count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 20</span>\n cursor<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'DAAACgGBGedb3Vx__9sKAAIZ5g4QENc99AcAAwAAIAIAAA\'</span>\n seenTweetIds<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'1867041249938530657\', \'1867041249938530659\']</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n features<span class="token operator">:</span> Features<span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Features</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n articles_preview_enabled<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n view_counts_everywhere_api_enabled<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n <span class="token comment">
1// ...</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"And here is a simplified ",t.createElement(s.strong,null,"response")," body type (we'll dive deeper into the response sub-types below):"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n responseObjects<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackActions<span class="token operator">:</span> TimelineAction<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineItem</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'tweet-1867041249938530657\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1866561576636152411\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineItem\'</span><span class="token punctuation">;</span>\n itemContent<span class="token operator">:</span> TimelineTweet<span class="token punctuation">;</span>\n feedbackInfo<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'-1378668161\']</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineTweet</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTweet\'</span><span class="token punctuation">;</span>\n tweet_results<span class="token operator">:</span> <span class="token punctuation">{</span>\n result<span class="token operator">:</span> Tweet<span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineCursor</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'cursor-top-1867041249938530657\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1866961576813152212\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineCursor\'</span><span class="token punctuation">;</span>\n value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA\'</span>\n cursorType<span class="token operator">:</span> <span class="token string">
1\'Top\'</span> <span class="token operator">|</span> <span class="token string">\'Bottom\'</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">ActionKey</span> <span class="token operator">=</span> <span class="token builtin">string</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,'It is interesting to note here, that "getting" the data is done via "POSTing", which is not common for the REST-like API but it is common for a GraphQL-like API.'),"\n",t.createElement(s.blockquote,null,"\n",t.createElement(s.p,null,"It is worth mentioning that sometimes data fetching in RESTful APIs can still be done via ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}})," requests. See the ",t.createElement(s.a,{href:"https://learn.microsoft.com/en-us/rest/api/azure/devops/git/commits/get-commits-batch?view=azure-devops-rest-7.2&tabs=HTTP"},"Get Commits Batch")," API from Azure as an example. However, one could arguably assume that such an approach is either an anti-pattern or a corner case to address specific RESTful limitations (e.g., URL length limits, avoiding HTTP-level caching, preventing unwanted logging of data in the URL, or other reasons)."),"\n",t.createElement(s.p,null,"Most of the time, RESTful APIs are pretty picky on how HTTP methods like ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">GET</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">PUT</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">PATCH</code>'}}),", and ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">DELETE</code>'}})," are used. What you would ",t.createElement(s.a,{href:"https://aws.amazon.com/what-is/restful-api/"},"typically see")," is:"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">GET</code>'}})," â Fetches resources from the server (e.g., retrieves a user entity)"),"\n",t.createElement(s.li,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}})," â Creates new resources (e.g., adds a new user)."),"\n",t.createElement(s.li,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">PUT</code>'}})," â Completely updates a resource (e.g., overwrites the entire user profile)."),"\n",t.createElement(s.li,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">PATCH</code>'}})," â Partially updates a resource (e.g., updates the user's phone number while leaving the rest of the profile unchanged)."),"\n",t.createElement(s.li,null,t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">DELETE</code>'}})," â Deletes a resource (e.g., removes a user from the database)."),"\n"),"\n",t.createElement(s.p,null,"These constraints are considered a strength of RESTful APIs because such a standardized approach makes it easier to understand and use any RESTful API (engineers and users know what to expect). However, it can also create confusion, such as deciding which method to use when a user:"),"\n",t.createElement(s.ol,null,"\n",t.createElement(s.li,null,"Requests a password reset, and then"),"\n",t.createElement(s.li,null,"Resets the password by clicking the link in the email."),"\n"),"\n",t.createElement(s.p,null,"Should this be a POST, PUT, PATCH, or something else?"),"\n"),"\n",t.createElement(s.p,null,"Additionally, in the case of the X API, the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">graphql</code>'}})," part of the URL indicates that X uses a GraphQL-based API."),"\n",t.createElement(s.p,null,"I'm using the word ",t.createElement(s.em,null,'"flavor"')," here because the request body itself doesn't look like a pure ",t.createElement(s.a,{href:"https://graphql.org/learn/queries/"},"GraphQL query"),", where we may describe the required response structure, listing all the properties we want to fetch:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="graphql"><pre class="language-graphql"><code class="language-graphql"><span class="token comment"># An example of a pure GraphQL request structure that is *not* being used in the X API.</span>\n<span class="token punctuation">{</span>\n <span class="token object">tweets</span> <span class="token punctuation">{</span>\n <span class="token property">id</span>\n <span class="token property">description</span>\n <span class="token property">created_at</span>\n <span class="token object">medias</span> <span class="token punctuation">{</span>
1\n <span class="token property">kind</span>\n <span class="token property">url</span>\n <span class="token comment"># ...</span>\n <span class="token punctuation">}</span>\n <span class="token object">author</span> <span class="token punctuation">{</span>\n <span class="token property">id</span>\n <span class="token property">name</span>\n <span class="token comment"># ...</span>\n <span class="token punctuation">}</span>\n <span class="token comment"># ...</span>\n <span class="token punctuation">}</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"The assumption here is that the home timeline API is not a pure GraphQL API, but is a ",t.createElement(s.strong,null,"mix of several approaches"),'. Passing the parameters in a POST request like this seems closer to the "functional" RPC call. But at the same time, it seems like the GraphQL features might be used somewhere on the backend behind the ',t.createElement(s.em,null,"HomeTimeline")," endpoint handler/controller. A mix like this might also be caused by a legacy code or some sort of ongoing migration. But again, these are just my speculations."),"\n",t.createElement(s.p,null,"You may also notice that the same ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineRequest.queryId</code>'}})," is used in the API URL as well as in the API request body. This queryId is most probably generated on the backend, then it gets embedded in the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">main.js</code>'}})," bundle, and then it is used when fetching the data from the backend. It is hard for me to understand how this ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}})," is used exactly since X's backend is a black box in our case. But, again, the speculation here might be that, it might be needed for some sort of performance optimization (re-using some pre-computed query results?), caching (Apollo related?), debugging (join logs by queryId?), or tracking/tracing purposes."),"\n",t.createElement(s.p,null,"One assumption here is that the actual GraphQL query might be just prepared (but not yet executed) on the server side and mapped to a specific ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}}),". Then, the client (i.e. the browser or the mobile app) will send the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}}),' along with some additional "dynamic" data (like pagination cursor) to the backend, which, in turn, would fetch the query by its ',t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}}),", augment the prepared query with a dynamic data and then would finally execute the actual GraphQL query. This may be useful, for example, in the following cases:"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"it may potentially save some traffic, since less data is being transmitted with each query,"),"\n",t.createElement(s.li,null,'it may take into account some security considerations and hide some query parameters or details from the user (the "secret" parameters will be attached on the backend side and are not visible in the browser\'s network tab),'),"\n",t.createElement(s.li,null,"it may allow the backend to have a full control over the actual GraphQL query structure","\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"there are many times when query structure may need to be changed (i.e. A/B testing, API v1->v2 migration, maybe some performance issues mitigation (like having a default sorting on field ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">A</code>'}})," is faster than on field ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">B</code>'}}),"), and other cases)"),"\n",t.createElement(s.li,null,"if such query adaptation may be performed solely on the backend without involving the web and mobile (iOS + Android) teams, it may have some benef
1its: 1 team (backend) and 1 deployment (backend service), instead of 3 teams (frontend, backend, mobile) and 4 deployments (backend, frontend, iOS, Android). This would require a generation of a new ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}})," and some mechanisms to pass this new ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}})," to the clients without them being re-build every time though."),"\n"),"\n"),"\n",t.createElement(s.li,null,"caching (not on the API-gateway level)?"),"\n"),"\n",t.createElement(s.p,null,"But again, the points above, are just mine speculations and assumptions. I can't be sure that this is how the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">queryId</code>'}})," is used by X team."),"\n",t.createElement(s.p,null,"It is also interesting to note, that the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineResponse</code>'}}
1)," contains not a list of tweets, but rather a list of ",t.createElement(s.strong,null,"instructions"),", like ",t.createElement(s.em,null,'"add a tweet to the timeline"')," (see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineAddEntries</code>'}})," type), or ",t.createElement(s.em,null,'"terminate the timeline"')," (see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineTerminateTimeline</code>'}})," type)."),"\n",t.createElement(s.p,null,"The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineAddEntries</code>'}})," instruction itself may also contain different types of entities:"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Tweets â see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineItem</code>'}})," type"),"\n",t.createElement(s.li,null,"Cursors â see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineCursor</code>'}})," type"),"\n",t.createElement(s.li,null,"Conversations/comments/threads â see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineModule</code>'}})," type"),"\n"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token comment">// ...</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"This is interesting from the extendability point of view since it allows a wider variety of what can be rendered in the home timeline without tweaking the API too much."),"\n",t.createElement(s.h3,{id:"pagination",style:{position:"relative"}},"Pagination",t.createElement(s.a,{href:"#pagination","aria-label":"pagination permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineRequest.variables.count</code>'}})," property sets how many tweets we want to fetch at once (per page). The default is 20. However, more than 20 tweets can be returned in the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineAddEntries.entries</code>'}})," array. For example, the array might contain 37 entries for the first page load, because it includes tweets (29), pinned tweets (1), promoted tweets (5), and pagination cursors (2). I'm not sure why there are 29 regular tweets with the requested count of 20 though."),"\n",t.createElement(s.p,null,"The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineRequest.variables.cursor</code>'}})," is responsible for the cursor-based pagination."),"\n",t.createElement(s.blockquote,null,"\n",t.createElement(s.p,null,'"',t.createElement(s.strong,null,"Cursor pagination"),' is most often used for real-time data due to the frequency new records are added and because when reading data you often see the latest results first. It eliminates the possibility of skipping items and displaying the same item more than once. In cursor-based pagination, a constant pointer (or cursor) is used to keep track of where in the data set the next items should be fetched from." See the ',t.createElement(s.a,{href:"https://stackoverflow.com/questions/55744926/offset-pagination-vs-cursor-pagination"},"Offset pagination vs Cursor pagination")," thread for the context."),"\n"),"\n",t.createElement(s.p,null,"When fetching the list of tweets for the first time the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineRequest.variables.cursor</code>'}})," is empty, since we want to fetch the top tweets from the default (most probably pre-computed) list of personalized tweets."),"\n",t.createElement(s.p,null,"However, in the response, along with the tweet data, the backend also returns the cursor entries. Here is the response type hierarchy: ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineResponse â TimelineAddEntries â TimelineCursor</code>'}}),":"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>
1\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token comment">// ...</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here (tweets + cursors)</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineCursor</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineCursor\'</span><span class="token punctuation">;</span>\n value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA\' <-- Here</span>\n cursorType<span class="token operator">:</span> <span class="token string">\'Top\'</span> <span class="token operator">|</span> <span class="token string">\'Bottom\'</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,'Every page contains the list of tweets along with "top" and "bottom" cursors:'),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<span\n class="gatsby-resp-image-wrapper"\n style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; "\n >
1\n <span\n class="gatsby-resp-image-background-image"\n style="padding-bottom: 61.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHaEXSwgP/EABsQAAIBBQAAAAAAAAAAAAAAAAECEAMREhQh/9oACAEBAAEFAslueQ1MbJj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAADAAMAAAAAAAAAAAAAAAABEBECEiH/2gAIAQEABj8C1orGXX//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhMWH/2gAIAQEAAT8h6XlujUQYppNYaYnUf//aAAwDAQACAAMAAAAQYM//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCH/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABsQAAICAwEAAAAAAAAAAAAAAAERACExQWFR/9oACAEBAAE/EFKZGjDLkoSHTzKccPd0BgqBHjmWTrUMwz//2Q==\'); background-size: cover; display: block;"\n ></span>\n <img\n class="gatsby-resp-image-image"\n alt="Cursors"\n title=""\n src="/static/b9a993f40d6f81a18d5315a05893fdb1/a2510/03-cursors.jpg"\n srcset="/static/b9a993f40d6f81a18d5315a05893fdb1/0479a/03-cursors.jpg 250w,\n/static/b9a993f40d6f81a18d5315a05893fdb1/41099/03-cursors.jpg 500w,\n/static/b9a993f40d6f81a18d5315a05893fdb1/a2510/03-cursors.jpg 1000w,\n/static/b9a993f40d6f81a18d5315a05893fdb1/c58a3/03-cursors.jpg 1500w,\n/static/b9a993f40d6f81a18d5315a05893fdb1/3acf0/03-cursors.jpg 2000w"\n sizes="(max-width: 1000px) 100vw, 1000px"\n style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n loading="lazy"\n decoding="async"\n />\n </span>'}}),"\n",t.createElement(s.p,null,'After the page data is loaded, we can go from the current page in both directions and fetch either the "previous/older" tweets using the "bottom" cursor or the "next/newer" tweets using the "top" cursor. My assumption is that fetching the "next" tweets using the "top" cursor happens in two cases: when the new tweets were added while the user is still reading the current page, or when the user starts scrolling the feed upwards (and there are no cached entries or if the previous entries were deleted for the performance reasons).'),"\n",t.createElement(s.p,null,"The X's cursor itself might look like this: ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">DAABCgABGemI6Mk__9sKAAIZ6MSYG9fQGwgAAwAAAAIAAA</code>'}}),". In some API designs, the cursor may be a Base64 encoded string that contains the id of the last entry in the list, or the timestamp of the last seen entry. For example: ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">eyJpZCI6ICIxMjM0NTY3ODkwIn0= --\x3e {"id": "1234567890"}</code>'}}),", and then, this data is used to query the database accordingly. In the case of X API, it looks like the cursor is being Base64 decoded into some custom binary sequence that might require some further decoding to get any meaning out of it (i.e. via the Protobuf message definitions). Since we don't know if it is a ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.proto</code>'}})," encoding and also we don't know the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">.proto</code>'}})," message definition we may just assume that the backend knows how to query the next batch of tweets based on the cursor string."),"\n",t.createElement(s.p,null,"The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineResponse.variables.seenTweetIds</code>'}})," parameter is used to inform the server about which tweets from the currently active page of the infinite scrolling the client has already seen. This most probably helps ensure that the server does not include duplicate tweets in subsequent pages of results."),"\n",t.createElement(s.h2,{id:"linkedhierarchical-entities",style:{position:"relative"}}
1,"Linked/hierarchical entities",t.createElement(s.a,{href:"#linkedhierarchical-entities","aria-label":"linkedhierarchical entities permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"One of the challenges to be solved in the APIs like home timeline (or Home Feed) is to figure out how to return the linked or hierarchical entities (i.e. ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">tweet â user</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">tweet â media</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">media â author</code>'}}),", etc):"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Should we only return the list of tweets first and then fetch the dependent entities (like user details) in a bunch of separate queries on-demand?"),"\n",t.createElement(s.li,null,"Or should we return all the data at once, increasing the time and the size of the first load, but saving the time for all subsequent calls?","\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Do we need to normalize the data in this case to reduce the payload size (i.e. when the same user is an author of many tweets and we want to avoid repeating the user data over and over again in each tweet entity)?"),"\n"),"\n"),"\n",t.createElement(s.li,null,"Or should it be a combination of the approaches above?"),"\n"),"\n",t.createElement(s.p,null,"Let's see how X handles it."),"\n",t.createElement(s.p,null,"Earlier in the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineTweet</code>'}})," type the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Tweet</code>'}})," sub-type was used. Let's see how it looks:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token comment">// ...</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">
1\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineItem</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineItem\'</span><span class="token punctuation">;</span>\n itemContent<span class="token operator">:</span> TimelineTweet<span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token comment">// ...</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineTweet</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTweet\'</span><span class="token punctuation">;</span>\n tweet_results<span class="token operator">:</span> <span class="token punctuation">{</span>\n result<span class="token operator">:</span> Tweet<span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// A Tweet entity</span>\n<span class="token keyword">type</span> <span class="token class-name">Tweet</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'Tweet\'</span><span class="token punctuation">;</span>\n core<span class="token operator">:</span> <span class="token punctuation">{</span>\n user_results<span class="token operator">:</span> <span class="token punctuation">{</span>\n result<span class="token operator">:</span> User<span class="token punctuation">;</span> <span class="token comment">// <-- Here (a dependent User entity)</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n legacy<span class="token operator">:</span> <span class="token punctuation">{</span>\n full_text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n <span class="token comment">// ...</span>\n entities<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">// <-- Here (a dependent Media entities)</span>\n media<span class="token operator">:</span> Media<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n hashtags<span class="token operator">:</span> Hashtag<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n urls<span class="token operator">:</span> Url<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n user_mentions<span class="token operator">:</span> UserMention<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// A User entity</span>\n<span class="token keyword">type</span> <span class="token class-name">User</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'User\'</span><span class="token punctuation">;</span>\n id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'VXNlcjoxNDUxM4ADSG44MTA4NDc4OTc2\'</span>\n <span class="token comment">
1// ...</span>\n legacy<span class="token operator">:</span> <span class="token punctuation">{</span>\n location<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'San Francisco\'</span>\n name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'John Doe\'</span>\n <span class="token comment">// ...</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// A Media entity</span>\n<span class="token keyword">type</span> <span class="token class-name">Media</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n <span class="token comment">// ...</span>\n source_user_id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\' <-- Here (the dependant user is being mentioned by its ID)</span>\n url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://t.co/X78dBgtrsNU\'</span>\n features<span class="token operator">:</span> <span class="token punctuation">{</span>\n large<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n medium<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n small<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n orig<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n sizes<span class="token operator">:</span> <span class="token punctuation">{</span>\n large<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n medium<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n small<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n thumb<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n video_info<span class="token operator">:</span> VideoInfo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"What's interesting here is that most of the dependent data like ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">tweet â media</code>'}})," and ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">tweet â author</code>'}})," is embedded into the response on the first call (no subsequent queries)."),"\n",t.createElement(s.p,null,"Also, the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">User</code>'}})," and ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media</code>'}})," connections with ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Tweet</code>'}})," entities are not normalized (if two tweets have the same author, their data will be repeated in each tweet object). But it seems like it should be ok, since in the scope of the home timeline for a specific user the tweets will be authored by many authors and repetitions are possible but sparse."),"\n",t.createElement(s.p,null,"My assumption was that the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">UserTweets</code>'}})," API (that we don't cover here), which is responsible for fetching the tweets of ",t.createElement(s.em,null,"one particular user")," will handle it differently, but, apparently, it is not the case. The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">UserTweets</code>'}})," returns the list of tweets of the same user and embeds the same user data over and over again for each tweet. It's interesting. Maybe the simplicity of the approach beats some data size overhead (maybe user data is considered pretty small in size). I'm not sure."),"\n",t.createElement(s.p,null,"Another observation about the entities' relationship is that the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media</code>'}})," entity also has a link to the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">User</code>'}})," (the author). But it does it not via direct entity embedding as the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Tweet</code>'}})," entity does, but rather it links via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media.source_user_id_str</code>'}})," property."),"\n",t.createElement(s.p,null,'The "comments" (which are also the "tweets" by their nature) for each "tweet" in the home timeline are not fetched at all. To see the tweet thread the user must click on the tweet to see its detailed view. The tweet thread will be fetched by calling the ',t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TweetDetail</code>'}}),' endpoint (more about it in the "Tweet detail page" section below).'),"\n",t.createElement(s.p,null,"Another entity that each ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Tweet</code>'}})," has is ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">FeedbackActions</code>'}}),' (i.e. "Recommend less often" or "See fewer"). The way the ',t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">FeedbackActions</code>'}})," are stored in the response object is different from the way the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">User</code>'}})," and ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media</code>'}})," objects are stored. While the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">User</code>'}})," and ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media</code>'}})," entities are part of the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Tweet</code>'}}),", the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">FeedbackActions</code>'}})," are stored separately in ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineItem.content.feedbackInfo.feedbackKeys</code>'}})," array and are linked via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ActionKey</code>'}}),". That was a slight surprise for me since it doesn't seem to be the case that any action is re-usable. It looks like one action is used for one particular tweet only. So it seems like the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">FeedbackActions</code>'}})," could be embedded into each tweet in the same way as ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">Media</code>'}})," entities. But I might be missing some hidden complexity here (like the fact that each action can have children actions)."),"\n",t.createElement(s.p,null,'More details about the actions are in the "Tweet actions" section below.'),"\n",t.createElement(s.h2,{id:"sorting",style:{position:"relative"}}
1,"Sorting",t.createElement(s.a,{href:"#sorting","aria-label":"sorting permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"The sorting order of the timeline entries is defined by the backend via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sortIndex</code>'}})," properties:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineCursor</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1866961576813152212\' <-- Here</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineCursor\'</span><span class="token punctuation">;</span>\n value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n cursorType<span class="token operator">:</span> <span class="token string">\'Top\'</span> <span class="token operator">|</span> <span class="token string">\'Bottom\'</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineItem</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1866561576636152411\' <-- Here</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineItem\'</span><span class="token punctuation">;</span>\n itemContent<span class="token operator">:</span> TimelineTweet<span class="token punctuation">;</span>\n feedbackInfo<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineModule</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'73343543020642838441\' <-- Here</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineModule\'</span><span class="token punctuation">;</span>\n items<span class="token operator">:</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span>\n item<span class="token operator">:</span> TimelineTweet<span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n displayType<span class="token operator">:</span> <span class="token string">\'VerticalConversation\'</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"The ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sortIndex</code>'}}
1)," itself might look something like this ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'1867231621095096312'</code>"}}),". It likely corresponds directly to or is derived from a ",t.createElement(s.a,{href:"https://en.wikipedia.org/wiki/Snowflake_ID"},"Snowflake ID"),"."),"\n",t.createElement(s.blockquote,null,"\n",t.createElement(s.p,null,'Actually most of the IDs you see in the response (tweet IDs) follow the "Snowflake ID" convention and look like ',t.createElement(s.span,{dangerouslySetInnerHTML:{__html:"<code class=\"language-text\">'1867231621095096312'</code>"}}),"."),"\n"),"\n",t.createElement(s.p,null,"If this is used to sort entities like tweets, the system leverages the inherent chronological sorting of Snowflake IDs. Tweets or objects with a higher sortIndex value (a more recent timestamp) appear higher in the feed, while those with lower values (an older timestamp) appear lower in the feed."),"\n",t.createElement(s.p,null,"Hereâs the step-by-step decoding of the Snowflake ID (in our case the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">sortIndex</code>'}}),") ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">1867231621095096312</code>'}}),":"),"\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Extract the ",t.createElement(s.strong,null,"Timestamp"),":","\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"The timestamp is derived by right-shifting the Snowflake ID by 22 bits (to remove the lower 22 bits for data center, worker ID, and sequence): ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">1867231621095096312 â 445182709954</code>'}})),"\n"),"\n"),"\n",t.createElement(s.li,null,"Add ",t.createElement(s.strong,null,"Twitter's Epoch"),":","\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Adding Twitter's custom epoch (1288834974657) to this timestamp gives the UNIX timestamp in milliseconds: ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">
1445182709954 + 1288834974657 â 1734017684611ms</code>'}})),"\n"),"\n"),"\n",t.createElement(s.li,null,"Convert to a ",t.createElement(s.strong,null,"human-readable date"),":","\n",t.createElement(s.ul,null,"\n",t.createElement(s.li,null,"Converting the UNIX timestamp to a UTC datetime gives: ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">1734017684611ms â 2024-12-12 15:34:44.611 (UTC)</code>'}})),"\n"),"\n"),"\n"),"\n",t.createElement(s.p,null,"So we can assume here that the tweets in the home timeline are sorted chronologically."),"\n",t.createElement(s.h2,{id:"tweet-actions",style:{position:"relative"}},"Tweet actions",t.createElement(s.a,{href:"#tweet-actions","aria-label":"tweet actions permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,'Each tweet has an "Actions" menu.'),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<span\n class="gatsby-resp-image-wrapper"\n style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; "\n >
1\n <span\n class="gatsby-resp-image-background-image"\n style="padding-bottom: 61.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2KSFgf/EABkQAQADAQEAAAAAAAAAAAAAAAIBAxAAEv/aAAgBAQABBQK1eK8ZhcTBz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABsQAAIBBQAAAAAAAAAAAAAAAAEQAAIhQVGx/9oACAEBAAY/AjVpkHMt1f/EABkQAQEBAQEBAAAAAAAAAAAAAAERACExgf/aAAgBAQABPyGJFwKYs77hyqCJd2b+p1u//9oADAMBAAIAAwAAABAQD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAQEBAAEFAQAAAAAAAAAAAAERADEhQVFxodH/2gAIAQEAAT8QQoQGLDnMD5fmAEB3TjASYxxPVz0LSfoOF1O//9k=\'); background-size: cover; display: block;"\n ></span>\n <img\n class="gatsby-resp-image-image"\n alt="Actions"\n title=""\n src="/static/f36b26cf9167c68df534014f9c745b8f/a2510/04-actions.jpg"\n srcset="/static/f36b26cf9167c68df534014f9c745b8f/0479a/04-actions.jpg 250w,\n/static/f36b26cf9167c68df534014f9c745b8f/41099/04-actions.jpg 500w,\n/static/f36b26cf9167c68df534014f9c745b8f/a2510/04-actions.jpg 1000w,\n/static/f36b26cf9167c68df534014f9c745b8f/c58a3/04-actions.jpg 1500w,\n/static/f36b26cf9167c68df534014f9c745b8f/3acf0/04-actions.jpg 2000w"\n sizes="(max-width: 1000px) 100vw, 1000px"\n style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n loading="lazy"\n decoding="async"\n />\n </span>'}}),"\n",t.createElement(s.p,null,"The actions for each tweet are coming from the backend in a ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineItem.content.feedbackInfo.feedbackKeys</code>'}})," array and are linked with the tweets via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">ActionKey</code>'}}),":"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n responseObjects<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackActions<span class="token operator">:</span> TimelineAction<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// <-- Here</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineItem</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineItem\'</span><span class="token punctuation">;</span>\n itemContent<span class="token operator">:</span> TimelineTweet<span class="token punctuation">;</span>\n feedbackInfo<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'-1378668161\'] <-- Here</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAction</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n key<span class="token operator">:</span> ActionKey<span class="token punctuation">;</span> <span class="token comment">// \'-609233128\'</span>\n value<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackType<span class="token operator">:</span> <span class="token string">\'NotRelevant\'</span> <span class="token operator">|</span> <span class="token string">\'DontLike\'</span> <span class="token operator">|</span> <span class="token string">\'SeeFewer\'</span><span class="token punctuation">;</span> <span class="token comment">
1// ...</span>\n prompt<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'This post isnât relevant\' | \'Not interested in this post\' | ...</span>\n confirmation<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Thanks. Youâll see fewer posts like this.\'</span>\n childKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'1192182653\', \'-1427553257\'], i.e. NotInterested -> SeeFewer</span>\n feedbackUrl<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'/2/timeline/feedback.json?feedback_type=NotRelevant&action_metadata=SRwW6oXZadPHiOczBBaAwPanEwE%3D\'</span>\n hasUndoAction<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n icon<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Frown\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"It is interesting here that this flat array of actions is actually a tree (or a graph? I didn't check), since each action may have child actions (see the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineAction.value.childKeys</code>'}})," array). This makes sense, for example, when after the user clicks on the ",t.createElement(s.strong,null,'"Don\'t Like"')," action, the follow-up might be to show the ",t.createElement(s.strong,null,'"This post isnât relevant"')," action, as a way of explaining why the user doesn't like the tweet."),"\n",t.createElement(s.h2,{id:"tweet-detail-page",style:{position:"relative"}},"Tweet detail page",t.createElement(s.a,{href:"#tweet-detail-page","aria-label":"tweet detail page permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"Once the user would like to see the tweet detail page (i.e. to see the thread of comments/tweets), the user clicks on the tweet and the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">GET</code>'}})," request to the following endpoint is performed:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">GET https://x.com/i/api/graphql/{query-id}/TweetDetail?variables={"focalTweetId":"1867231621095096312","referrer":"home","controller_data":"DACABBSQ","rankingMode":"Relevance","includePromotedContent":true,"withCommunity":true}&features={"articles_preview_enabled":true}</code></pre></div>'}}),"\n",t.createElement(s.p,null,"I was curious here why the list of tweets is being fetched via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}})," call, but each tweet detail is fetched via the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">GET</code>'}})," call. Seems inconsistent. Especially keeping in mind that similar query parameters like ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">query-id</code>'}}),", ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">features</code>'}}),", and others this time are passed in the URL and not in the request body. The response format is also similar and is re-using the types from the list call. I'm not sure why is that. But again, I'm sure I might be might be missing some background complexity here."),"\n",t.createElement(s.p,null,"Here are the simplified response body types:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">TweetDetailResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n threaded_conversation_with_injections_v2<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">
1\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineTerminateTimeline</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineTerminateTimeline\'</span><span class="token punctuation">,</span>\n direction<span class="token operator">:</span> <span class="token string">\'Top\'</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineModule</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'conversationthread-58668734545929871193\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867231621095096312\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineModule\'</span><span class="token punctuation">;</span>\n items<span class="token operator">:</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token comment">// \'conversationthread-1866876425669871193-tweet-1866876038930951193\'</span>\n item<span class="token operator">:</span> TimelineTweet<span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// Comments to the tweets are also tweets</span>\n displayType<span class="token operator">:</span> <span class="token string">\'VerticalConversation\'</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"The response is pretty similar (in its types) to the list response, so we won't for too long here."),"\n",t.createElement(s.p,null,'One interesting nuance is that the "comments" (or conversations) of each tweet are actually other tweets (see the ',t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineModule</code>'}})," type). So the tweet thread looks very similar to the home timeline feed by showing the list of ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">TimelineTweet</code>'}})," entries. This looks elegant. A good example of a universal and re-usable approach to the API design."),"\n",t.createElement(s.h2,{id:"liking-the-tweet",style:{position:"relative"}},"Liking the tweet",t.createElement(s.a,{href:"#liking-the-tweet","aria-label":"liking the tweet permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"When a user likes the tweet, the ",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<code class="language-text">POST</code>'}})," request to the following endpoint is being performed:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet</code></pre></div>'}}),"\n",t.createElement(s.p,null,"Here is the ",t.createElement(s.strong,null,"request")," body types:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">FavoriteTweetRequest</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n variables<span class="token operator">:</span> <span class="token punctuation">{</span>\n tweet_id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n queryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'lI07N61twFgted2EgXILM7A\'</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"Here is the ",t.createElement(s.strong,null,"response")," body types:"),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token keyword">type</span> <span class="token class-name">FavoriteTweetResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n favorite_tweet<span class="token operator">:</span> <span class="token string">\'Done\'</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span>\n<span class="token punctuation">}</span></code></pre></div>'}}),"\n",t.createElement(s.p,null,"Looks straightforward and also resembles the RPC-like approach to the API design."),"\n",t.createElement(s.h2,{id:"conclusion",style:{position:"relative"}}
1,"Conclusion",t.createElement(s.a,{href:"#conclusion","aria-label":"conclusion permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"We have touched on some basic parts of the home timeline API design by looking at X's API example. I made some assumptions along the way to the best of my knowledge. I believe some things I might have interpreted incorrectly and I might have missed some complex nuances. But even with that in mind, I hope you got some useful insights from this high-level overview, something that you could apply in your next API Design session."),"\n",t.createElement(s.p,null,"Initially, I had a plan to go through similar top-tech websites to get some insights from Facebook, Reddit, YouTube, and others and to collect battle-tested best practices and solutions. I'm not sure if I'll find the time to do that. Will see. But it could be an interesting exercise."),"\n",t.createElement(s.h2,{id:"upd-2025-12-18",style:{position:"relative"}},"UPD (2025-12-18)",t.createElement(s.a,{href:"#upd-2025-12-18","aria-label":"upd 2025 12 18 permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.blockquote,null,"\n",t.createElement(s.p,null,"Based on the X thread from ",t.createElement(s.a,{href:"https://x.com/UriGoldshtein/status/1869403702668214451"},"@UriGoldshtein")),"\n",t.createElement(s.p,null,'"He (trekhleb) says they are not using "pure GraphQL" because they are not sending the query over the network - but that\'s exactly how you should use GraphQL - using "Persisted/Trusted queries". That makes GraphQL a developer time tool, not a runtime feature. Which means you can basically think of each query is a REST endpoint, but instead of writing code to create controllers and endpoints, it is done automatically for you using static, type-safe, graph queries that at build time turn into endpoints and a secure allow list. It also means you don\'t need to upload the whole query body, just the query id. You 100% should use trusted documents for you GraphQL API and X is obviously doing it!"'),"\n",t.createElement(s.p,null,'"He (trekhleb) was asking "I was curious here why the list of tweets is being fetched via the POST call, but each tweet detail is fetched via the GET call". My uneducated guess is that because a single tweet is more static content compared to the list of tweets that might change based on many things, with GET it could use simple, popular and existing caching mechanisms. That is another misconception about GraphQL - it can use both GET and POST and as we discussed before - if you use (and you should) trust documents, you basically create GET endpoints that are easily cacheable."'),"\n"),"\n",t.createElement(s.h2,{id:"appendix-all-types-in-one-place",style:{position:"relative"}},"Appendix: All types in one place",t.createElement(s.a,{href:"#appendix-all-types-in-one-place","aria-label":"appendix all types in one place permalink",className:"gatsby-remark-autolink-header-anchor after"},t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>'}}))),"\n",t.createElement(s.p,null,"For the reference, I'm adding all types in one go here. You may also find all types in ",t.createElement(s.a,{href:"https://github.com/trekhleb/trekhleb.github.io/blob/master/src/posts/2024/api-design-x-home-timeline/types/x.ts"},"types/x.ts")," file."),"\n",t.createElement(s.span,{dangerouslySetInnerHTML:{__html:'<div class="gatsby-highlight" data-language="typescript"><pre class="language-typescript"><code class="language-typescript"><span class="token comment">/**\n * This file contains the simplified types for X\'s (Twitter\'s) home timeline API.\n *\n * These types are created for exploratory purposes, to see the current implementation\n * of the X\'s API, to see how they fetch Home Feed, how they do a pagination and sorting,\n * and how they pass the hierarchical entities (posts, media, user info, etc).\n *\n * Many properties and types are omitted for simplicity.\n */</span>\n\n<span class="token comment">// POST https://x.com/i/api/graphql/{query-id}/HomeTimeline</span>\n<span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">TimelineRequest</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n queryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'s6ERr1UxkxxBx4YundNsXw\'</span>\n variables<span class="token operator">:</span> <span class="token punctuation">{</span>\n count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 20</span>\n cursor<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'DAAACgGBGedb3Vx__9sKAAIZ5g4QENc99AcAAwAAIAIAAA\'</span>\n seenTweetIds<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'1867041249938530657\', \'1867041249938530658\']</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n features<span class="token operator">:</span> Features<span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// POST https://x.com/i/api/graphql/{query-id}/HomeTimeline</span>\n<span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">TimelineResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>
1\n home<span class="token operator">:</span> <span class="token punctuation">{</span>\n home_timeline_urt<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n responseObjects<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackActions<span class="token operator">:</span> TimelineAction<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet</span>\n<span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">FavoriteTweetRequest</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n variables<span class="token operator">:</span> <span class="token punctuation">{</span>\n tweet_id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n queryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'lI07N6OtwFgted2EgXILM7A\'</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token comment">// POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet</span>\n<span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">FavoriteTweetResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n favorite_tweet<span class="token operator">:</span> <span class="token string">\'Done\'</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span>\n<span class="token punctuation">}</span>\n\n<span class="token comment">// GET https://x.com/i/api/graphql/{query-id}/TweetDetail?variables={"focalTweetId":"1867041249938530657","referrer":"home","controller_data":"DACABBSQ","rankingMode":"Relevance","includePromotedContent":true,"withCommunity":true}&features={"articles_preview_enabled":true}</span>\n<span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token class-name">TweetDetailResponse</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n data<span class="token operator">:</span> <span class="token punctuation">{</span>\n threaded_conversation_with_injections_v2<span class="token operator">:</span> <span class="token punctuation">{</span>\n instructions<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineAddEntries <span class="token operator">|</span> TimelineTerminateTimeline<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Features</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n articles_preview_enabled<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n view_counts_everywhere_api_enabled<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n <span class="token comment">
1// ...</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAction</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n key<span class="token operator">:</span> ActionKey<span class="token punctuation">;</span> <span class="token comment">// \'-609233128\'</span>\n value<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackType<span class="token operator">:</span> <span class="token string">\'NotRelevant\'</span> <span class="token operator">|</span> <span class="token string">\'DontLike\'</span> <span class="token operator">|</span> <span class="token string">\'SeeFewer\'</span><span class="token punctuation">;</span> <span class="token comment">// ...</span>\n prompt<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'This post isnât relevant\' | \'Not interested in this post\' | ...</span>\n confirmation<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Thanks. Youâll see fewer posts like this.\'</span>\n childKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'1192182653\', \'-1427553257\'], i.e. NotInterested -> SeeFewer</span>\n feedbackUrl<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'/2/timeline/feedback.json?feedback_type=NotRelevant&action_metadata=SRwW6oXZadPHiOczBBaAwPanEwE%3D\'</span>\n hasUndoAction<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n icon<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Frown\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineAddEntries</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineAddEntries\'</span><span class="token punctuation">;</span>\n entries<span class="token operator">:</span> <span class="token punctuation">(</span>TimelineItem <span class="token operator">|</span> TimelineCursor <span class="token operator">|</span> TimelineModule<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineTerminateTimeline</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n type<span class="token operator">:</span> <span class="token string">\'TimelineTerminateTimeline\'</span><span class="token punctuation">,</span>\n direction<span class="token operator">:</span> <span class="token string">\'Top\'</span><span class="token punctuation">,</span>\n<span class="token punctuation">}</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineCursor</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'cursor-top-1867041249938530657\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867231621095096312\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineCursor\'</span><span class="token punctuation">;</span>\n value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA\'</span>\n cursorType<span class="token operator">:</span> <span class="token string">
1\'Top\'</span> <span class="token operator">|</span> <span class="token string">\'Bottom\'</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineItem</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'tweet-1867041249938530657\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867231621095096312\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineItem\'</span><span class="token punctuation">;</span>\n itemContent<span class="token operator">:</span> TimelineTweet<span class="token punctuation">;</span>\n feedbackInfo<span class="token operator">:</span> <span class="token punctuation">{</span>\n feedbackKeys<span class="token operator">:</span> ActionKey<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [\'-1378668161\']</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineModule</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'conversationthread-1867041249938530657\'</span>\n sortIndex<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867231621095096312\'</span>\n content<span class="token operator">:</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTimelineModule\'</span><span class="token punctuation">;</span>\n items<span class="token operator">:</span> <span class="token punctuation">{</span>\n entryId<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token comment">// \'conversationthread-1867041249938530657-tweet-1867041249938530657\'</span>\n item<span class="token operator">:</span> TimelineTweet<span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">// Comments to the tweets are also tweets</span>\n displayType<span class="token operator">:</span> <span class="token string">\'VerticalConversation\'</span><span class="token punctuation">,</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">TimelineTweet</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'TimelineTweet\'</span><span class="token punctuation">;</span>\n tweet_results<span class="token operator">:</span> <span class="token punctuation">{</span>\n result<span class="token operator">:</span> Tweet<span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Tweet</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'Tweet\'</span><span class="token punctuation">;</span>\n core<span class="token operator">:</span> <span class="token punctuation">{</span>\n user_results<span class="token operator">:</span> <span class="token punctuation">{</span>\n result<span class="token operator">:</span> User<span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n views<span class="token operator">:</span> <span class="token punctuation">{</span>\n count<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'13763\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n legacy<span class="token operator">:</span> <span class="token punctuation">{</span>\n bookmark_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 358</span>\n created_at<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Tue Dec 10 17:41:28 +0000 2024\'</span>\n conversation_id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n display_text_range<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [0, 58]</span>\n favorite_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 151</span>\n full_text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// "How I\'d promote my startup, if I had 0 followers (Part 1)"</span>\n lang<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'en\'</span>\n quote_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n reply_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n retweet_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n user_id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n entities<span class="token operator">:</span> <span class="token punctuation">{</span>\n media<span class="token operator">:</span> Media<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n hashtags<span class="token operator">:</span> Hashtag<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n urls<span class="token operator">:</span> Url<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n user_mentions<span class="token operator">:</span> UserMention<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">User</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n __typename<span class="token operator">:</span> <span class="token string">\'User\'</span><span class="token punctuation">;</span>\n id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'VXNlcjoxNDUxM4ADSG44MTA4NDc4OTc2\'</span>
1\n rest_id<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n is_blue_verified<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n profile_image_shape<span class="token operator">:</span> <span class="token string">\'Circle\'</span><span class="token punctuation">;</span> <span class="token comment">// ...</span>\n legacy<span class="token operator">:</span> <span class="token punctuation">{</span>\n following<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n created_at<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Thu Oct 21 09:30:37 +0000 2021\'</span>\n description<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'I help startup founders double their MRR with outside-the-box marketing cheat sheets\'</span>\n favourites_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 22195</span>\n followers_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 25658</span>\n friends_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n location<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'San Francisco\'</span>\n media_count<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>\n name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'John Doe\'</span>\n profile_banner_url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://pbs.twimg.com/profile_banners/4863509452891265813/4863509\'</span>\n profile_image_url_https<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://pbs.twimg.com/profile_images/4863509452891265813/4863509_normal.jpg\'</span>\n screen_name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'johndoe\'</span>\n url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://t.co/dgTEddFGDd\'</span>\n verified<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Media</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n display_url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'pic.x.com/X7823zS3sNU\'</span>\n expanded_url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://x.com/johndoe/status/1867041249938530657/video/1\'</span>\n ext_alt_text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Image of two bridges.\'</span>\n id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n indices<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [93, 116]</span>\n media_key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'13_2866509231399826944\'</span>\n media_url_https<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://pbs.twimg.com/profile_images/1867041249938530657/4863509_normal.jpg\'</span>\n source_status_id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n source_user_id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'1867041249938530657\'</span>\n type<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'video\'</span>\n url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://t.co/X78dBgtrsNU\'</span>\n features<span class="token operator">:</span> <span class="token punctuation">{</span>\n large<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n medium<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n small<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n orig<span class="token operator">:</span> <span class="token punctuation">{</span> faces<span class="token operator">:</span> FaceGeometry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n sizes<span class="token operator">:</span> <span class="token punctuation">{</span>\n large<span class="token operator">:</span>
1 MediaSize<span class="token punctuation">;</span>\n medium<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n small<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n thumb<span class="token operator">:</span> MediaSize<span class="token punctuation">;</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n video_info<span class="token operator">:</span> VideoInfo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">UserMention</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n id_str<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'98008038\'</span>\n name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'Yann LeCun\'</span>\n screen_name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'ylecun\'</span>\n indices<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [115, 122]</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Hashtag</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n indices<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [257, 263]</span>\n text<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">Url</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n display_url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'google.com\'</span>\n expanded_url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'http://google.com\'</span>\n url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://t.co/nZh3aF0Aw6\'</span>\n indices<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [102, 125]</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">VideoInfo</span> <span class="token operator">=</span> <span class="token punctuation">{</span>\n aspect_ratio<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// [427, 240]</span>\n duration_millis<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 20000</span>\n variants<span class="token operator">:</span> <span class="token punctuation">{</span>\n bitrate<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> <span class="token comment">// 288000</span>\n content_type<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'application/x-mpegURL\' | \'video/mp4\' | ...</span>\n url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span> <span class="token comment">// \'https://video.twimg.com/amplify_video/18665094345456w6944/pl/-ItQau_LRWedR-W7.m3u8?tag=14\'</span>\n <span class="token punctuation">}</span><span class="token punctuation">;</span>\n<span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">FaceGeometry</span> <span class="token operator">=</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> y<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> h<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> w<span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">MediaSize</span> <span class="token operator">=</span> <span class="token punctuation">{</span> h<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> w<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span> resize<span class="token operator">:</span> <span class="token string">\'fit\'</span> <span class="token operator">|</span> <span class="token string">
1\'crop\'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>\n\n<span class="token keyword">type</span> <span class="token class-name">ActionKey</span> <span class="token operator">=</span> <span class="token builtin">string</span><span class="token punctuation">;</span></code></pre></div>'}}))}var p=function(n){void 0===n&&(n={});const{wrapper:s}=Object.assign({},(0,e.ah)(),n.components);return s?t.createElement(s,n,t.createElement(o,n)):o(n)},c=a(7084);const l=n=>{const{data:s,children:a}=n;return t.createElement(c.Z,{post:s},a)};function i(n){return t.createElement(l,n,t.createElement(p,n))}},7084:function(n,s,a){"use strict";a.d(s,{Z:function(){return d}});var e=a(7294),t=a(6669),o=a(4363),p=a(550),c=a(9737),l=a(5103),i=a(2717),r=a(8288);var u=n=>{var s,a,t,u,k,m,d;const{post:h,children:g}=n,A=null!=h&&null!==(s=h.mdx)&&void 0!==s&&null!==(a=s.frontmatter)&&void 0!==a&&a.date?e.createElement(l.Z,{className:"mr-6 mb-6 text-gray-500 text-sm"},e.createElement(o.v,{className:"mr-1"}),null==h||null===(t=h.mdx)||void 0===t?void 0:t.frontmatter.date):null,y=null!=h&&null!==(u=h.mdx)&&void 0!==u&&u.timeToRead?e.createElement(l.Z,null,e.createElement(p.T,{className:"mr-1"}),(0,i.j)(null==h||null===(k=h.mdx)||void 0===k?void 0:k.timeToRead)," min to read"):null;return e.createElement("div",{className:"flex flex-col items-center"},e.createElement("article",{className:"w-full prose prose-sm sm:prose overflow-hidden prose-red",style:{maxWidth:"860px"}},e.createElement(c.Z,null,(null===(m=h.mdx)||void 0===m||null===(d=m.frontmatter)||void 0===d?void 0:d.title)||""),e.createElement(l.Z,null,A,y),e.createElement(r.Z,null,g)))},k=a(7943),m=a(5677);var d=n=>{var s,a,o,p,c,l,i,d,h,g,A;const{post:y,children:b}=n;return e.createElement(t.Z,null,e.createElement(k.ZP,{title:(null===(s=y.mdx)||void 0===s||null===(a=s.frontmatter)||void 0===a?void 0:a.title)||"",titleMode:k.H0,description:(null===(o=y.mdx)||void 0===o||null===(p=o.frontmatter)||void 0===p?void 0:p.summary)||"",image:(null===(c=y.mdx)||void 0===c||null===(l=c.frontmatter)||void 0===l||null===(i=l.cover)||void 0===i||null===(d=i.childImageSharp)||void 0===d||null===(h=d.gatsbyImageData)||void 0===h||null===(g=h.images)||void 0===g||null===(A=g.fallback)||void 0===A?void 0:A.src)||"",type:k.Sp}),e.createElement(u,{post:y},b),e.createElement("div",{className:"flex flex-row justify-center items-center mt-16"},e.createElement("div",{className:"max-w-md"},e.createElement(r.Z,null,e.createElement(m.Z,null)))))}},2091:function(n,s,a){"use strict";a.d(s,{Iy:function(){return c},hW:function(){return l}});var e=a(7294),t=a(9881),o=a(3150),p=a(5103);const c="info",l="error",i={[c]:e.createElement(t.L,{size:18}),[l]:e.createElement(o.C,{size:18})},r={[c]:"text-blue-600 bg-blue-100",[l]:"text-red-600 bg-red-100"};s.ZP=n=>{const{children:s,type:a}=n;return s?e.createElement("div",{className:"py-3 px-4 rounded-md "+r[a]},e.createElement(p.Z,null,e.createElement("div",{className:"mr-3"},i[a]),e.createElement("div",{className:"text-sm"},s))):null}},8288:function(n,s,a){"use strict";var e=a(4578),t=a(7294),o=a(2091);let p=function(n){function s(s){var a;return(a=n.call(this,s)||this).state={hasError:!1},a}(0,e.Z)(s,n),s.getDerivedStateFromError=function(){return{hasError:!0}};var a=s.prototype;return a.componentDidCatch=function(n,s){console.error(n,s),this.setState({hasError:!0})},a.render=function(){const{children:n}=this.props,{hasError:s}=this.state;return s?t.createElement(o.ZP,{type:o.hW},"Component has crashed"):n},s}(t.Component);s.Z=p},9737:function(n,s,a){"use strict";var e=a(7294),t=a(4519);s.Z=n=>{const{children:s,className:a=""}=n,o="mb-6 uppercase font-extrabold "+a;return e.createElement(t.Z,{level:t._.h1,className:o},s)}},5677:function(n,s,a){"use strict";var e=a(7294);s.Z=n=>{const{withHeader:s=!0}=n,a="border py-2 px-3 mb-3 rounded border-gray-300 border-solid appearance-none",t=s?e.createElement("h1",{className:"text-grey-darkest uppercase font-bold text-xl mb-3"},"Subscribe to the Newsletter"):null;return e.createElement("div",{className:"bg-white rounded-md shadow-md p-8"},t,e.createElement("p",{className:"text-sm mb-3"},"Get my latest posts and project updates by email"),e.createElement("form",{action:"https://dev.us1.list-manage.com/subscribe/post?u=7714f14ff32085c685da2cfaa&id=53ffa81463",method:"post",className:"flex flex-col"},e.createElement("input",{placeholder:"First Name",type:"text",name:"FNAME",className:a,required:!0}),e.createElement("input",{placeholder:"Email",type:"email",name:"EMAIL",className:a,required:!0}),e.createElement("div",{className:"hidden","aria-hidden":"true"},e.createElement("input",{type:"text",name:"b_7714f14ff32085c685da2cfaa_53ffa81463",tabIndex:-1})),e.createElement("input",{type:"submit",value:"Subscribe",className:"transition duration-200 ease-in-out bg-black text-white py-2 px-3 rounded shadow-sm cursor-pointer hover:bg-gray-800"}
1)))}},2717:function(n,s,a){"use strict";a.d(s,{D:function(){return t},j:function(){return e}});const e=n=>{if("number"!=typeof n)return null;return Math.ceil(1*n)||1},t=async n=>new Promise((s=>{setTimeout(s,n)}))},1151:function(n,s,a){"use strict";a.d(s,{ah:function(){return o}});var e=a(7294);const t=e.createContext({});function o(n){const s=e.useContext(t);return e.useMemo((()=>"function"==typeof n?n(s):{...s,...n}),[s,n])}}}]); 2//# sourceMappingURL=component---src-templates-post-tsx-content-file-path-src-posts-2024-api-design-x-home-timeline-index-mdx-d81440dab07cf00f66d6.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.