<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RSS Feed]]></title><description><![CDATA[Thoughts, links, and ideas.]]></description><link>https://jpwilliams.dev</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 01 Dec 2021 11:44:28 GMT</lastBuildDate><item><title><![CDATA[Fetching the latest release of a GitHub package with Cloudflare Workers]]></title><description><![CDATA[There's an example of this worker up at https://mod.jpwilliams.dev/github-release-version that can be used for any package like so: https…]]></description><link>https://jpwilliams.dev/cloudflare-worker-github-releases/</link><guid isPermaLink="false">https://jpwilliams.dev/cloudflare-worker-github-releases/</guid><pubDate>Fri, 17 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There&apos;s an example of this worker up at &lt;code&gt;https://mod.jpwilliams.dev/github-release-version&lt;/code&gt; that can be used for any package like so: &lt;a href=&quot;https://mod.jpwilliams.dev/github-release-version/jpwilliams/midi-mixer-releases&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mod.jpwilliams.dev/github-release-version/jpwilliams/midi-mixer-releases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When creating the website for &lt;a href=&quot;https://midi-mixer.jpwilliams.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIDI Mixer&lt;/a&gt;, I needed to be able to list the latest version available to download without making any hefty requests and without updating the site every time I updated the software.&lt;/p&gt;
&lt;p&gt;GitHub provides a wonderful &lt;a href=&quot;https://docs.github.com/en/rest&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;API&lt;/a&gt; that&apos;s exceedingly easy to use, meaning we can mock up a Cloudflare Worker that returns the data we need with not a huge amount of effort.&lt;/p&gt;
&lt;h3&gt;A Basic Worker&lt;/h3&gt;
&lt;p&gt;The simplest Worker we can produce is the one that Cloudflare provides as its default: a &quot;Hello World&quot; worker. It looks like this:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;javascript&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk11&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;fetch&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, (&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  event.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(event.request));&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt; * Respond to the request&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt; * &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;@param&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;{Request}&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;request&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt; */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, { status: &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We won&apos;t go in to Workers in-depth here, but they work very similarly (both in code and function) to a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ServiceWorker&lt;/a&gt;. If you&apos;ve never used Cloudflare Workers before, I&apos;d strongly suggest checking out their &lt;a href=&quot;https://developers.cloudflare.com/workers/quickstart&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Quick Start guide&lt;/a&gt; to get going, well, quickly.&lt;/p&gt;
&lt;p&gt;For our purposes, we&apos;re first going to need to figure out how to to retrieve the data we need.&lt;/p&gt;
&lt;h3&gt;The GitHub API&lt;/h3&gt;
&lt;p&gt;GitHub allows you to explore pieces of their API with no credentials, meaning we can instantly see what information we can get. After a quick look on the GitHub docs site I found the &lt;a href=&quot;https://docs.github.com/en/rest/reference/repos#get-the-latest-release&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Get the latest release&lt;/a&gt; endpoint. You can go and check out the response right now: &lt;a href=&quot;https://api.github.com/repos/jpwilliams/midi-mixer-releases/releases/latest&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://api.github.com/repos/jpwilliams/midi-mixer-releases/releases/latest&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&apos;s a lot of data there, but the key piece for us is the &lt;code&gt;tag_name&lt;/code&gt; property. The tags I use for versioning on GitHub are &lt;a href=&quot;https://semver.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;semver&lt;/a&gt;-compatible version strings prefixed with &lt;code&gt;v&lt;/code&gt;, so this is perfect to use as my version number.&lt;/p&gt;
&lt;p&gt;Now we know what we&apos;re looking for, let&apos;s add a function to our worker that&apos;ll pull the data we need.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;javascript&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;getVersion&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;targetUrl&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;`https://api.github.com/repos/${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}/${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}/releases/latest`&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  headers.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;accept&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;application/vnd.github.v3+json&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  headers.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;authorization&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;token YOUR_GITHUB_TOKEN&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  headers.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;user-agent&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;YOUR_GITHUB_USERNAME&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(targetUrl, { headers });&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; res.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;release)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;`No valid releases found for ${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}/${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}`&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(release.tag_name &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (err) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(err);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getVersion&lt;/code&gt; is a function that takes a &lt;code&gt;username&lt;/code&gt; and a &lt;code&gt;repo&lt;/code&gt;, and returns a response containing either the version string if it was successful or nothing (but still a &lt;code&gt;200 OK&lt;/code&gt; status) if it wasn&apos;t. You should handle failing cases depending on your use, but for me it made sense to just &quot;silently&quot; fail and return nothing.&lt;/p&gt;
&lt;p&gt;There are a few pieces to pick apart here.&lt;/p&gt;
&lt;p&gt;First, the headers. When calling the GitHub API from your browser, it realises this and lets you through (with a tight rate limit). When accessing it programmatically, however, GitHub needs some more love. For this to work you&apos;ll need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;Accept&lt;/code&gt; to &lt;code&gt;application/vnd.github.v3+json&lt;/code&gt;. This tells GitHub that you&apos;re intending to send an API request and are ready for the format it&apos;s delivered in.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;Authorization&lt;/code&gt; to &lt;code&gt;token YOUR_GITHUB_TOKEN&lt;/code&gt;. You can generate a token on your &lt;a href=&quot;https://github.com/settings/tokens&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Personal Access Tokens&lt;/a&gt; page. It&apos;ll need either the &lt;code&gt;repo&lt;/code&gt; or &lt;code&gt;public_repo&lt;/code&gt; permission depending on your needs.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;User-Agent&lt;/code&gt; to your username. GitHub actually &lt;a href=&quot;https://docs.github.com/en/rest/overview/resources-in-the-rest-api#user-agent-required&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;requires this&lt;/a&gt; as a means to provide accountability for particular API calls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we have our main code, we&apos;ll need to expand our &quot;Hello World&quot; &lt;code&gt;handleRequest&lt;/code&gt; to a much nicer version.&lt;/p&gt;
&lt;h3&gt;Handling the request&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;handleRequest&lt;/code&gt; needs to grab the data that we need to be able to send to &lt;code&gt;getVersion&lt;/code&gt;. In this Worker, I&apos;ve done this using the URL path. We can access the path really easily by using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;URL&lt;/code&gt;&lt;/a&gt; interface with the string URL we get in the request.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;javascript&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(request.url);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; url.pathname.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;username &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;repo) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;Invalid request. No username or repository specified. Format is /username/repo&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;        status: &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;getVersion&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(username, repo);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; res;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (err) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(err);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome! We use a &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; here to ensure that if anything goes wrong it&apos;s just logged and we return a silent failure. The only piece of code that might be a bit odd is how we extract &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;repo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Remember that a URL to access the current service I&apos;ve deployed looks like this: &lt;code&gt;https://mod.jpwilliams.dev/github-release-version/jpwilliams/midi-mixer-releases&lt;/code&gt;. If a request comes in to that URL, here&apos;s what happens to extract &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;repo&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;javascript&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// Request to: https://mod.jpwilliams.dev/github-release-version/jpwilliams/midi-mixer-release&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(request.url);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk8&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(url.pathname); &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// /github-release-version/jpwilliams/midi-mixer-releases&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;url.pathname&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  .&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// github-release-version/jpwilliams/midi-mixer-releases&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  .&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// [&amp;quot;github-release-version&amp;quot;, &amp;quot;jpwilliams&amp;quot;, &amp;quot;midi-mixer-releases&amp;quot;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  .&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;); &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// [&amp;quot;jpwilliams&amp;quot;, &amp;quot;midi-mixer-releases&amp;quot;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That, along with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;destructuring&lt;/a&gt; gets us our values regardless of the base path (&lt;code&gt;github-release-version&lt;/code&gt; in this case) it&apos;s deployed on!&lt;/p&gt;
&lt;h3&gt;Sorted.&lt;/h3&gt;
&lt;p&gt;With that, we&apos;re done! A really quick, really clean method of grabbing the plain-text version number of the latest release of any GitHub repository.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&apos;re using this in production (or with &lt;a href=&quot;https://developers.cloudflare.com/workers/tooling/wrangler&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Wrangler&lt;/a&gt;), please don&apos;t put your GitHub API token in the code. Instead, use &lt;a href=&quot;https://developers.cloudflare.com/workers/tooling/wrangler/secrets/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Secrets&lt;/a&gt;. For an example of this, see the repo below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The complete code base for this with TypeScript and &lt;a href=&quot;https://developers.cloudflare.com/workers/tooling/wrangler&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Wrangler&lt;/a&gt; included are available at &lt;a href=&quot;https://github.com/jpwilliams/github-release-version&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;jpwilliams/github-release-version&lt;/a&gt;. Check out &lt;a href=&quot;https://midi-mixer.jpwilliams.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIDI Mixer&lt;/a&gt; too if you fancy seeing a live implementation!&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
  .github-light {
    color: #24292e;
    background-color: #fff;
  }
  .github-light .mtk11 { color: #6F42C1; }
  .github-light .mtk1 { color: #24292E; }
  .github-light .mtk9 { color: #032F62; }
  .github-light .mtk12 { color: #E36209; }
  .github-light .mtk6 { color: #D73A49; }
  .github-light .mtk7 { color: #6A737D; }
  .github-light .mtk8 { color: #005CC5; }
  .github-light .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Nullish Short-Circuit Assignment in TypeScript 4.0 (beta)]]></title><description><![CDATA[On June 26th 2020, TypeScript 4.0 Beta was announced. The update brings a myriad of useful changes, but one such addition is something you…]]></description><link>https://jpwilliams.dev/nullish-short-circuit-assignment-in-typescript-4.0/</link><guid isPermaLink="false">https://jpwilliams.dev/nullish-short-circuit-assignment-in-typescript-4.0/</guid><pubDate>Thu, 02 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On June 26th 2020, &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript 4.0 Beta&lt;/a&gt; was announced. The update brings a myriad of useful changes, but one such addition is something you&apos;ll quickly get in to the habit of using every day: Nullish Short-Circuit Assignment.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;typescript&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// A function that adds a post.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// If the user in the post doesn&amp;#39;t exist, add a fake `0` user instead.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// It&amp;#39;s valid to submit the fake `0` user too.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// Old&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;addPost&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; post.user &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;!==&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;number&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    post.user &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// New&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;addPost&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  post.user &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;??=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ve used &lt;code&gt;a += b&lt;/code&gt; before to add to a number or extend a string without having to write &lt;code&gt;a = a + b&lt;/code&gt;. What&apos;s frustrating, though, is having to check if a variable is nullish (&lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;) before assigning it. TypeScript 4.0 adds three new assignment operators: &lt;code&gt;&amp;#x26;&amp;#x26;=&lt;/code&gt;, &lt;code&gt;||=&lt;/code&gt;, and &lt;code&gt;??=&lt;/code&gt;. Using these alongside the Nullish Coalescing added in &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript 3.7&lt;/a&gt;, even more boilerplate code can be stripped down!&lt;/p&gt;
&lt;p&gt;In the grand scheme it&apos;s a small improvement, but the new code is definitely shorter and more readable.&lt;/p&gt;
&lt;p&gt;What about a simpler example? In the block above we wanted to be able to parse &lt;code&gt;0&lt;/code&gt; as valid input, so we couldn&apos;t just do a falsey check. What if we just wanted to use nullish coalescing (&lt;code&gt;??&lt;/code&gt;) &lt;em&gt;without&lt;/em&gt; the short-circuit assignment (&lt;code&gt;??=&lt;/code&gt;)? That&apos;s pretty succint too, right?&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;typescript&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;parseConfig&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  config.setting &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; config.setting &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;set&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This doesn&apos;t look &lt;em&gt;too&lt;/em&gt; bad. We have to repeat &lt;code&gt;config.setting&lt;/code&gt; which could lead to some spelling mistakes and therefore unintended results, but it&apos;s still pretty readable and concise. What this is vulnerable to, however, is &lt;em&gt;setters&lt;/em&gt;. If you&apos;re not familiar, a &lt;code&gt;setter&lt;/code&gt; is a function that binds an object property to a function to be called when there&apos;s an attempt to set that property. For example, I could ensure that setting the &lt;code&gt;username&lt;/code&gt; property on my object updates the &lt;code&gt;profileUrl&lt;/code&gt; field appropriately:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;javascript&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.profileUrl &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;`https://example.com/@${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}`&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;obj.username &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;FoobarBaggins&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// obj.profileUrl === &amp;quot;https://example.com/@FoobarBaggins&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because a &lt;code&gt;setter&lt;/code&gt; is just a function, it could potentially perform a computationally-expensive task without the developer realising. In our Nullish Coalescing examples, performing &lt;code&gt;obj.foo = obj.foo ?? &quot;bar&quot;;&lt;/code&gt; will &lt;em&gt;always&lt;/em&gt; call the &lt;code&gt;foo&lt;/code&gt; &lt;code&gt;setter&lt;/code&gt;, regardless of if &lt;code&gt;obj.foo&lt;/code&gt; was nullish or not. If we use a short-circuit (&lt;code&gt;obj.foo ??= &quot;bar&quot;;&lt;/code&gt;), the &lt;code&gt;setter&lt;/code&gt; will only be called if &lt;code&gt;obj.foo&lt;/code&gt; was nullish. Nice!&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;typescript&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;obj.username &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; obj.username &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;FoobarBaggins&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;; &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// calls setter every time&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;obj.username &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;??=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;quot;FoobarBaggins&amp;quot;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;; &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// only calls setter if obj.username nullish&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I highly recommend you check out the wonderful &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript 4.0 Beta&lt;/a&gt; announcement for all the nitty-gritty of the new features. You can use the beta today with &lt;code&gt;npm i -D typescript@beta&lt;/code&gt;, though check if it&apos;s fully released when you&apos;re reading this!&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
  .github-light {
    color: #24292e;
    background-color: #fff;
  }
  .github-light .mtk7 { color: #6A737D; }
  .github-light .mtk6 { color: #D73A49; }
  .github-light .mtk1 { color: #24292E; }
  .github-light .mtk11 { color: #6F42C1; }
  .github-light .mtk12 { color: #E36209; }
  .github-light .mtk8 { color: #005CC5; }
  .github-light .mtk9 { color: #032F62; }
  .github-light .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to unpack the return type of a Promise in TypeScript]]></title><description><![CDATA[TypeScript 4.5 (released on November 17th, 2021) introduced the Awaited type that solves this problem. If you can use Awaited, use that…]]></description><link>https://jpwilliams.dev/how-to-unpack-the-return-type-of-a-promise-in-typescript/</link><guid isPermaLink="false">https://jpwilliams.dev/how-to-unpack-the-return-type-of-a-promise-in-typescript/</guid><pubDate>Sun, 05 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-5&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript 4.5&lt;/a&gt; (released on November 17th, 2021) introduced the &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-5/#awaited-type&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;Awaited&lt;/code&gt;&lt;/a&gt; type that solves this problem. If you can use &lt;code&gt;Awaited&lt;/code&gt;, use that instead of the below method. &lt;em&gt;- edited 1st December, 2021&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While making &lt;a href=&quot;https://github.com/jpwilliams/distributed-promise&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/distributed-promise&lt;/a&gt; I came across an interesting requirement: I needed to acquire the return type of an &lt;code&gt;async&lt;/code&gt; function &lt;em&gt;after&lt;/em&gt; it had been resolved. I needed to &lt;em&gt;unwrap&lt;/em&gt; a promise.&lt;/p&gt;
&lt;p&gt;For example, an &lt;code&gt;async&lt;/code&gt; function that simply returns &lt;code&gt;&quot;foo&quot;&lt;/code&gt; would have the return type &lt;code&gt;Promise&amp;#x3C;string&gt;&lt;/code&gt;. For my purposes, I needed &lt;em&gt;just&lt;/em&gt; the &lt;code&gt;string&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;It turns out that this is very possible since &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript 2.8&lt;/a&gt; and the introduction of the &lt;code&gt;infer&lt;/code&gt; keyword back in March 2018. For the requirements I had above, I ended up with the following &lt;code&gt;type&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;ts&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;AsyncReturnType&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could be used like so:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;ts&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T0&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;AsyncReturnType&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;() &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// string&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This type, given a function type, will return either the type of the resolved promise if a promise is returned or the straight return type of the function. This is really cool, but it confused the hell out of me at first glance, so let&apos;s step through.&lt;/p&gt;
&lt;p&gt;First, this makes heavy use of &lt;code&gt;infer&lt;/code&gt;, which we can use to extract types from matching signatures. A simple example of this would be making a type that takes &lt;code&gt;any[]&lt;/code&gt; as input &lt;code&gt;T&lt;/code&gt; and returns the &lt;code&gt;any&lt;/code&gt;. We can do this in exactly the same way we did with the function signatures above:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;ts&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Unwrap&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)[] &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There, we check if &lt;code&gt;T&lt;/code&gt; matches the pattern &lt;code&gt;something[]&lt;/code&gt; and use &lt;code&gt;infer&lt;/code&gt; to extract that &lt;code&gt;something&lt;/code&gt;. If it does, return our &lt;code&gt;something&lt;/code&gt;, otherwise just return &lt;code&gt;T&lt;/code&gt;. This brings us to the second major point: notice the &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;:&lt;/code&gt; ternary operators being used. Their use here is exactly the same as in regular ol&apos; JavaScript. Cool, huh?&lt;/p&gt;
&lt;p&gt;Now we have that under our belt, understanding the original type is a bit easier:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;ts&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// create a generic type&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;AsyncReturnType&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// if T matches this signature and returns a Promise, extract&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// U (the type of the resolved promise) and use that, or...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// if T matches this signature and returns anything else,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// extract the return value U and use that, or...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// if everything goes to hell, return an `any`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice! Though I don&apos;t need it for my uses, a good catch-all might be to include the &lt;code&gt;Promise&lt;/code&gt; type itself.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;ts&quot; data-index=&quot;4&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Unwrap&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;extends&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;infer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;T&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned above, this is used within &lt;a href=&quot;https://github.com/jpwilliams/distributed-promise&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/distributed-promise&lt;/a&gt;, a small library that can be used to distribute promises across the network and multiple processes.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
  .github-light {
    color: #24292e;
    background-color: #fff;
  }
  .github-light .mtk6 { color: #D73A49; }
  .github-light .mtk1 { color: #24292E; }
  .github-light .mtk11 { color: #6F42C1; }
  .github-light .mtk12 { color: #E36209; }
  .github-light .mtk8 { color: #005CC5; }
  .github-light .mtk7 { color: #6A737D; }
  .github-light .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Using EventEmitters to resolve Promises from afar in Node.js]]></title><description><![CDATA[A common-but-powerful pattern I've come to frequent is the utilisation of EventEmitter alongside a Promise, enabling me to resolve said…]]></description><link>https://jpwilliams.dev/using-eventemitters-to-resolve-promises-from-afar-in-nodejs/</link><guid isPermaLink="false">https://jpwilliams.dev/using-eventemitters-to-resolve-promises-from-afar-in-nodejs/</guid><pubDate>Wed, 11 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A common-but-powerful pattern I&apos;ve come to frequent is the utilisation of &lt;code&gt;EventEmitter&lt;/code&gt; alongside a &lt;code&gt;Promise&lt;/code&gt;, enabling me to resolve said promise from an entirely different part of my code base.&lt;/p&gt;
&lt;p&gt;To demonstrate, let&apos;s revisit the basics of both an &lt;code&gt;EventEmitter&lt;/code&gt; and a &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;EventEmitter&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;EventEmitter&lt;/code&gt; is an object which can be used to &quot;emit&quot; named events which, in turn, cause &lt;code&gt;Function&lt;/code&gt; objects (&quot;listeners&quot;) to be called. Here&apos;s a speedy example that will log &lt;code&gt;&quot;foo&quot;&lt;/code&gt;, &lt;code&gt;&quot;bar&quot;&lt;/code&gt;, and &lt;code&gt;&quot;baz&quot;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;events&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// this is a core Node package&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;emitter&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;() &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// create our emitter&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;emitter.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk7&quot;&gt;// console.log any data coming in from the &amp;#39;data&amp;#39; event&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// send data&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;emitter.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;emitter.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;emitter.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;baz&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have that in place, as long as we pass that same &lt;code&gt;emitter&lt;/code&gt; around, different parts of our application can communicate asynchronously and without relying on a direct connection.&lt;/p&gt;
&lt;h2&gt;Promise&lt;/h2&gt;
&lt;p&gt;For a &lt;code&gt;Promise&lt;/code&gt;, let&apos;s look at the most recent way of defining them. Those used to newer versions of Node.js (and JavaScript in general) will know of &lt;code&gt;async&lt;/code&gt; functions:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;doSomeWork&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;doSomeMore&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;getUnorderedData&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	data.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; data&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A function with the &lt;code&gt;async&lt;/code&gt; prefix will always return a &lt;code&gt;Promise&lt;/code&gt; and has the added benefit of allowing us to use the &lt;code&gt;await&lt;/code&gt; keyword to &quot;block&quot; while some asynchronous work is completed.&lt;/p&gt;
&lt;p&gt;For our purposes, though, we&apos;re going to use a different way of defining a &lt;code&gt;Promise&lt;/code&gt;: with callbacks. Using &lt;code&gt;new Promise&lt;/code&gt;, we can get two functions (&lt;code&gt;resolve&lt;/code&gt; and &lt;code&gt;reject&lt;/code&gt;) to use to, well, either resolve or reject the &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;		&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	})&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;You can be cheeky here, too, and make the callback an &lt;code&gt;async&lt;/code&gt; function to get the goodness of &lt;code&gt;await&lt;/code&gt; while still having the tighter control of the two functions.&lt;/em&gt; &lt;/p&gt;
&lt;h2&gt;Combining the two&lt;/h2&gt;
&lt;p&gt;So how does this tie in with our &lt;code&gt;EventEmitter&lt;/code&gt;? Well now that we have a function that resolves our &lt;code&gt;Promise&lt;/code&gt;, it&apos;s really easy to use an emitter inside. We can use the &lt;code&gt;once&lt;/code&gt; method to register a one-time listener that removes itself once it&apos;s invoked.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;waitForNextData&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;		emitter.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, resolve)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	})&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🤯 Now we could call &lt;code&gt;waitForNextData&lt;/code&gt; which would return a &lt;code&gt;Promise&lt;/code&gt; which would resolve once some new &lt;code&gt;data&lt;/code&gt; came in via our emitter!&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;4&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;waitForNextData&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an incredibly simple combination of JavaScript&apos;s asynchronous toolkit, but provides some sneaky tactics to use across larger projects when direct communication between components is either difficult or ill-advised. It&apos;s used heavily in &lt;a href=&quot;https://github.com/jpwilliams/remit&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/remit&lt;/a&gt;, a microservices toolkit, to manage incoming and outgoing messages which may be received in a place far different from where they were sent. Also &lt;a href=&quot;https://github.com/jpwilliams/waitgroup&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/waitgroup&lt;/a&gt;, a tiny version of Golang&apos;s &lt;code&gt;WaitGroup&lt;/code&gt; with promises, which is a great mini example of this technique.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
  .github-light {
    color: #24292e;
    background-color: #fff;
  }
  .github-light .mtk6 { color: #D73A49; }
  .github-light .mtk1 { color: #24292E; }
  .github-light .mtk8 { color: #005CC5; }
  .github-light .mtk9 { color: #032F62; }
  .github-light .mtk7 { color: #6A737D; }
  .github-light .mtk11 { color: #6F42C1; }
  .github-light .mtk12 { color: #E36209; }
  .github-light .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Modularising GraphQL for scale]]></title><description><![CDATA[When fiddling around with GraphQL projects, I've frequently found it difficult to know where things should go. Just like naming variables…]]></description><link>https://jpwilliams.dev/modularising-graphql-for-scale/</link><guid isPermaLink="false">https://jpwilliams.dev/modularising-graphql-for-scale/</guid><pubDate>Sun, 24 Nov 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When fiddling around with GraphQL projects, I&apos;ve frequently found it difficult to know where things should go. Just like naming variables, naming and placing files is hard. The simplest method here is to bundle everything in to one file, but that quickly gets out of hand.&lt;/p&gt;
&lt;p&gt;Separate files means separate logical groups of relevant data. There are plenty of ways to easily view directory trees, but it&apos;s not so easy to bundle all of the mental state of a 3,000 line file in to your head. What if our entire GraphQL server could be viewed neatly, like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 829px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.24154589371981%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABrUlEQVQoz22SWY7bMBBEfZCRtVmyNm7arI2Uxk6cAEGQ3P8ylSY1dmJMPhokRfCpqrsOeSYheYcgEjj6Jbxj4dZH2XN6vqDvDfS8wiwr2laj6zR6KqkWd77QfV1rHOyDMpfIsgbxSX6G0j6xwM5gmQzm0aAjwDmf6PuENNsr+VgPQVhCVDUBW5wSCT+oCFi+qEzOA0F2mFXFxYK8mJH9pw6+X0CxloAd/JDjjRR5x9yp9D72aTagcbY0xsEQ8BVi1T6BQSSRJA2aeqS+jOAEj2KFkOqxnvOR+rwRSCOrJuRst5xm4wtsVxgweD4jKEctezA+Ioy4s27vbNkeXobvmPUPCPUO1XwBExua7isqvj7Btg5vXoE4ZkgTgTjgiBOFIGT/WN6H0tY3aPMT/XRDQxMVNVmXqwOWpLhkZldoH8QRA2MKedkSjL9M+REboa5Y1t8EvKLuZ8h2AZMzQd8h6Wequf0FBmSPMQkhR5zS+nMOaShWkTF3bNdfBN+cIquOidWBHraphyVsuE/UwySVLot7dIon0MZGKY37daNg3zEs30jd6tTZnhaVfg7nD6osMkYjHnqBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Example GraphQL wow tree&quot;
        title=&quot;Example GraphQL wow tree&quot;
        src=&quot;/static/2e0b3caee88a38a8067f1d5770214dd6/9d76a/graphql-example-tree.png&quot;
        srcset=&quot;/static/2e0b3caee88a38a8067f1d5770214dd6/661c4/graphql-example-tree.png 207w,
/static/2e0b3caee88a38a8067f1d5770214dd6/73926/graphql-example-tree.png 415w,
/static/2e0b3caee88a38a8067f1d5770214dd6/9d76a/graphql-example-tree.png 829w,
/static/2e0b3caee88a38a8067f1d5770214dd6/561da/graphql-example-tree.png 969w&quot;
        sizes=&quot;(max-width: 829px) 100vw, 829px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On the left here, we see the directory tree for the types in this example GraphQL server. Each type has its own directory with a &lt;code&gt;schema.graphql&lt;/code&gt; file defining it. For the simple &lt;code&gt;Date&lt;/code&gt; type, we&apos;ve only made two files - &lt;code&gt;schema.graphql&lt;/code&gt; and &lt;code&gt;resolvers.js&lt;/code&gt;. The files here are really simple:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// schema.graphql&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;scalar &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;Date&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// resolvers.js&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;GraphQLDate&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;graphql-date&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk8&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; GraphQLDate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While we&apos;ve separated those in to two files, with the code we&apos;ll be writing we could just as well place them in a single file, &lt;code&gt;Date.js&lt;/code&gt;, in place of the entire &lt;code&gt;Date&lt;/code&gt; folder:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk7&quot;&gt;// Date.js&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;GraphQLDate&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;graphql-date&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;`scalar Date`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; GraphQLDate&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk8&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; { schema, resolvers }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that we can keep exceedingly simple types in singular files where we can see the entirety of their functionality in a small number of lines. Larger types like &lt;code&gt;Message&lt;/code&gt;, though, we can split up as we see fit.&lt;/p&gt;
&lt;p&gt;This is perfectly doable, but there&apos;s a certain degree of mucking about that&apos;s required to get all of these schemas and resolvers in to place. Usually that means back to one massive file that &lt;code&gt;require&lt;/code&gt;s a hundred others.&lt;/p&gt;
&lt;p&gt;For ease of use, a better method of loading and managing GraphQL types, resolvers, and loaders is available as a package at &lt;a href=&quot;https://www.npmjs.com/package/@jpwilliams/graphql-modular-loader&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/graphql-modular-loader&lt;/a&gt;. An example of its use with &lt;a href=&quot;https://github.com/apollographql/apollo-server&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;apollographql/apollo-server&lt;/a&gt; is as follows:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container github-light&quot; data-language=&quot;js&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;ApolloServer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;apollo-server&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;@jpwilliams/graphql-modular-loader&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;typeDefs&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; } &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;&amp;#39;./types&amp;#39;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk6&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;ApolloServer&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;({ typeDefs, resolvers })&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;server.&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;({&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	port: &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;4000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;mtk11&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(({ &lt;/span&gt;&lt;span class=&quot;mtk12&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; }) &lt;/span&gt;&lt;span class=&quot;mtk6&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mtk8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;`🚀	Server ready at ${&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;mtk9&quot;&gt;}`&lt;/span&gt;&lt;span class=&quot;mtk1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;mtk1&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But let&apos;s slow up a second. If we use that package and separate our entire GraphQL functionality in to individual files, what are a few things we could gain?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ease of deletion.&lt;/strong&gt; Deleting code is a big part of cleaning up. Sometimes that means entire sections of a deprecated API. Here, you&apos;d delete the relevant folder and that&apos;s it. Done. Otherwise, you&apos;re digging through files hoping nothing secretly relies on what you&apos;ve done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of concern.&lt;/strong&gt; Building up the state of an application in your mind is a huge part of programming. If you can open a single file and only have to concentrate on that singular part, you&apos;re already half way to solving the problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Bookmarked&quot; code.&lt;/strong&gt; Searching for the exact part you need of a large file is not a nice experience. Comments and IDEs help with this, but nothing beats having your code organised in to small, relevant files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the aim of &lt;a href=&quot;https://www.npmjs.com/package/@jpwilliams/graphql-modular-loader&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@jpwilliams/graphql-modular-loader&lt;/a&gt;. Personally, it&apos;s helped me keep many projects shockingly orderly.&lt;/p&gt;
&lt;p&gt;The package supports every aspect of GraphQL that may need to be separated out: schemas, queries, mutations, subscriptions, field resolvers, and loaders. For a full look at the source code and up-to-date documentation, check out the &lt;a href=&quot;https://github.com/jpwilliams/graphql-modular-loader&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;jpwilliams/graphql-modular-loader&lt;/a&gt; GitHub repository.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
  .github-light {
    color: #24292e;
    background-color: #fff;
  }
  .github-light .mtk7 { color: #6A737D; }
  .github-light .mtk1 { color: #24292E; }
  .github-light .mtk8 { color: #005CC5; }
  .github-light .mtk6 { color: #D73A49; }
  .github-light .mtk9 { color: #032F62; }
  .github-light .mtk11 { color: #6F42C1; }
  .github-light .mtk12 { color: #E36209; }
  .github-light .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[It's all about trade-offs]]></title><description><![CDATA[I recently had a great discussion regarding WebSockets and HTTP/2 with a former colleague. During the discourse, they uttered one line so…]]></description><link>https://jpwilliams.dev/its-all-about-trade-offs/</link><guid isPermaLink="false">https://jpwilliams.dev/its-all-about-trade-offs/</guid><pubDate>Mon, 11 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently had a great discussion regarding WebSockets and HTTP/2 with a former colleague. During the discourse, they uttered one line so deeply-set in truth that it must be shared. Repeatedly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;It&apos;s all about trade-offs.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As someone who has been developing web applications for a little while (and as somebody who repeatedly rewrites them), I consistently find myself doubting the quality of the system at hand. It could be more performant. It could be more readable. It could be more developer-friendly or use the better (read: newer) technology X. These niggling thoughts often cause me to cave in and needlessly redesign a &lt;em&gt;working&lt;/em&gt; system. Sometimes it&apos;s better, sometimes it&apos;s not.&lt;/p&gt;
&lt;p&gt;Development as a whole is a mixed field. Its obscure mix of creativity and science is what attracts so many, yet it&apos;s that same trait that muddies the waters, encouraging &lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_triviality&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;bikeshedding&lt;/a&gt; and frequently instilling doubt in our minds. But remember, &lt;em&gt;it&apos;s all about trade-offs.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For me, system design - and therefore development - is about achieving a balance between two distinct states of nirvana: &lt;strong&gt;performance&lt;/strong&gt; and &lt;strong&gt;simplicity&lt;/strong&gt;. Fulfilling both of these states is very possible, but we&apos;re most often limited by one ugly constraint: &lt;strong&gt;time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; covers &lt;em&gt;how fast it runs&lt;/em&gt;. If it&apos;s a service that delivers a response when asked a question, how quickly does it respond? How many questions can it handle at once?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt; can be read as how &lt;em&gt;understandable&lt;/em&gt; the system is. Does the system work in a clean and intuitive way? How easily would somebody else understand the system? Building something that&apos;s easily understood takes a lot more &lt;em&gt;time&lt;/em&gt; and consideration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt; is the major constraint. Deadlines, whether arbitrary or not, are everywhere and can&apos;t be ignored. Often, an application or feature is wanted to perform X and making the application prettier in ways the end user won&apos;t see isn&apos;t budgeted for.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;&lt;em&gt;Any fool can write code that a computer can understand. Good programmers write code that humans can understand.&lt;/em&gt;&quot; - Martin Fowler&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As a whole, we already abide by these forces unknowingly: the vast majority of developers use high-level languages like JavaScript and PHP that facilitate the creation of human-readable code. A program written in a high-level language will often be less performant than one written in low-level languages like C or Assembly, but readability, ease of use and ease of &lt;em&gt;change&lt;/em&gt; counts for &lt;em&gt;a lot&lt;/em&gt;, so we sacrifice these performance gains. Ergo, we trade &lt;em&gt;performance&lt;/em&gt; for &lt;em&gt;time&lt;/em&gt; and &lt;em&gt;simplicity&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Very few projects, applications or architectures provide the freedom to reach the peak of performance and simplicity simultaneously, so we face trade-offs. We use X technology because its more well-known, so easier to find help with, though it may be less performant. We use a nasty-looking regex to perform a particular search because it&apos;s blisteringly quick, though it&apos;s insanely difficult to adapt to changing requirements. We use microservices because they&apos;re great for defining service boundaries and ownership, though they&apos;re hell to deploy.&lt;/p&gt;
&lt;p&gt;Dynamically-typed versus statically-typed, relational versus non-relational, object-oriented versus procedural, monoliths versus microservices - there are no right answers. If you can reasonably justify the balance of performance, complexity and time in your application then your choices are correct. In most cases, a solution is not invalid because it fails to maximise a single aspect of its potential; it is the sum of its aspects.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  .grvsc-container {
    overflow: auto;
    position: relative;
    -webkit-overflow-scrolling: touch;
    padding-top: 1rem;
    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));
    padding-bottom: 1rem;
    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));
    border-radius: 8px;
    border-radius: var(--grvsc-border-radius, 8px);
    font-feature-settings: normal;
    line-height: 1.4;
  }
  
  .grvsc-code {
    display: table;
  }
  
  .grvsc-line {
    display: table-row;
    box-sizing: border-box;
    width: 100%;
    position: relative;
  }
  
  .grvsc-line &gt; * {
    position: relative;
  }
  
  .grvsc-gutter-pad {
    display: table-cell;
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  .grvsc-gutter {
    display: table-cell;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter::before {
    content: attr(data-content);
  }
  
  .grvsc-source {
    display: table-cell;
    padding-left: 1.5rem;
    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));
    padding-right: 1.5rem;
    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));
  }
  
  .grvsc-source:empty::after {
    content: &apos; &apos;;
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none;
  }
  
  .grvsc-gutter + .grvsc-source {
    padding-left: 0.75rem;
    padding-left: calc(var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem)) / 2);
  }
  
  /* Line transformer styles */
  
  .grvsc-has-line-highlighting &gt; .grvsc-code &gt; .grvsc-line::before {
    content: &apos; &apos;;
    position: absolute;
    width: 100%;
  }
  
  .grvsc-line-diff-add::before {
    background-color: var(--grvsc-line-diff-add-background-color, rgba(0, 255, 60, 0.2));
  }
  
  .grvsc-line-diff-del::before {
    background-color: var(--grvsc-line-diff-del-background-color, rgba(255, 0, 20, 0.2));
  }
  
  .grvsc-line-number {
    padding: 0 2px;
    text-align: right;
    opacity: 0.7;
  }
  
&lt;/style&gt;</content:encoded></item></channel></rss>