<?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[Edoardo Scibona]]></title><description><![CDATA[Edoardo Scibona]]></description><link>https://www.edoardoscibona.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 18:03:54 GMT</lastBuildDate><atom:link href="https://www.edoardoscibona.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[🦕 Running your first Deno script]]></title><description><![CDATA[In this post, we are going from zero to running a small but nontrivial script that fetches comments from Hacker News in real time using Deno, a new runtime for JavaScript and TypeScript created by Ryan Dahl, the original creator of Node.js.
What is D...]]></description><link>https://www.edoardoscibona.com/running-your-first-deno-script</link><guid isPermaLink="true">https://www.edoardoscibona.com/running-your-first-deno-script</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Edoardo Scibona]]></dc:creator><pubDate>Thu, 08 Jul 2021 16:21:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625322884293/4n6qqHwMX.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, we are going from zero to running a small but nontrivial script that fetches comments from Hacker News in real time using <a target="_blank" href="https://deno.land/">Deno</a>, a new runtime for JavaScript and <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> created by <a target="_blank" href="https://github.com/ry">Ryan Dahl</a>, the original creator of <a target="_blank" href="https://nodejs.org/">Node.js</a>.</p>
<h2 id="what-is-deno">What is Deno?</h2>
<p>Oversimplifying, we can say that Deno is an alternative to Node.js.</p>
<p>More precisely, we can read the following description on <a target="_blank" href="https://deno.land/">Deno's website</a>:</p>
<blockquote>
<p>Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.</p>
</blockquote>
<p>Let's delve deeper into three key characteristics that distinguish Deno from Node.js:</p>
<ul>
<li><p><strong>Simple</strong>: as we will soon see, Deno is simple to install, being shipped as a single executable file; Deno is also simple to use for small and larger scripts thanks to its powerful CLI, modern features and standard library; finally, learning Deno is also simple thanks to its short and execellent <a target="_blank" href="https://deno.land/manual">manual</a></p>
</li>
<li><p><strong>Modern</strong>: Deno is built with modern technologies and follows a modern philosophy; it natively supports <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> and <a target="_blank" href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/">ES modules</a>; it doesn't require a centralized package management solution; it also provides useful developer tools out of the box such as an opinionated source code formatter and a testing framework</p>
</li>
<li><p><strong>Secure</strong>: by default, Deno prevents access to critical resources such as files, network connections and environment variables; using these resources requires an explicit permission grant by the user</p>
</li>
</ul>
<p>Now that we have got to know Deno better, let's start using it.</p>
<h2 id="what-are-we-building">What are we building?</h2>
<p>We are going to write a script that reads the stream of comments posted to <a target="_blank" href="https://news.ycombinator.com/">Hacker News</a> in real time and prints them in the console.</p>
<p>We will use TypeScript to write the script and Deno to run it; prior experience in TypeScript or Deno is not required since we will go step by step.</p>
<p>I assume that you are using a Linux distribution where a text editor and a terminal are available, however you should be able to follow the same instructions on Windows or Mac with minimal differences.</p>
<p>When following along, you can refer to the <a target="_blank" href="https://gist.github.com/velut/6d0335c3ede885e11ce7c5768107d103">source code for the script</a>, integrating it with the <a target="_blank" href="https://deno.land/manual">Deno manual</a> and the <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/intro.html">TypeScript Handbook</a> if necessary.</p>
<h2 id="setup">Setup</h2>
<p>Let's start by installing Deno on our machine and verifying that it works.</p>
<p>According to <a target="_blank" href="https://deno.land/manual@v1.11.5/getting_started/installation">Deno's installation instructions</a>, we need to run the following command in our terminal:</p>
<pre><code class="lang-bash">curl -fsSL https://deno.land/x/install/install.sh | sh
</code></pre>
<p>The command above requires having both <code>curl</code> and <code>unzip</code> installed on our system.</p>
<p>After the command finishes running, we should see the following output:</p>
<pre><code class="lang-bash">Archive:  /home/&lt;your_username&gt;/.deno/bin/deno.zip
  inflating: /home/&lt;your_username&gt;/.deno/bin/deno
Deno was installed successfully to /home/&lt;your_username&gt;/.deno/bin/deno
Manually add the directory to your <span class="hljs-variable">$HOME</span>/.bash_profile (or similar)
  <span class="hljs-built_in">export</span> DENO_INSTALL=<span class="hljs-string">"/home/&lt;your_username&gt;/.deno"</span>
  <span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$DENO_INSTALL</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<p>As the message suggests, we need to edit (or create) the profile file used by our shell, for example <code>$HOME/.profile</code> or <code>$HOME/.bash_profile</code>, and add these two lines at the bottom:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> DENO_INSTALL=<span class="hljs-string">"/home/&lt;your_username&gt;/.deno"</span>
<span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$DENO_INSTALL</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<p>Make sure to paste the lines actually displayed in <strong>your terminal</strong> by Deno.</p>
<p>After saving the profile file and restarting the terminal, we should be able to write <code>deno</code> and press enter to see the following output:</p>
<pre><code class="lang-bash">$ deno
Deno 1.11.5
<span class="hljs-built_in">exit</span> using ctrl+d or close()
&gt;
</code></pre>
<p>As suggested, we can exit from Deno by pressing <code>Ctrl</code> and <code>D</code> together.</p>
<p>Additionally, running <code>deno run https://deno.land/std/examples/welcome.ts</code> in the terminal should display the following message:</p>
<pre><code class="lang-bash">$ deno run https://deno.land/std/examples/welcome.ts
Welcome to Deno!
</code></pre>
<p>If setting the <code>PATH</code> environment variable is not possible, we can still call <code>deno</code> by its relative path, for example:</p>
<pre><code class="lang-bash">$ .deno/bin/deno
Deno 1.11.5
<span class="hljs-built_in">exit</span> using ctrl+d or close()
&gt;
</code></pre>
<p>Now that Deno is correctly installed, we can start writing our script.</p>
<h2 id="step-1-create-the-maints-file">Step 1: Create the <code>main.ts</code> file</h2>
<p>In an empty directory, let's create a file named <code>main.ts</code> (the name <code>main</code> has no importance) that looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello from Deno!"</span>);
</code></pre>
<p>Let's open a terminal in the same directory and run <code>deno run main.ts</code> to see the following output:</p>
<pre><code class="lang-bash">$ deno run main.ts
Check file:///home/&lt;your_username&gt;/&lt;some_directory&gt;/main.ts
Hello from Deno!
</code></pre>
<p>As we can see, we don't need a <code>package.json</code> file or any external dependencies to run this TypeScript file.</p>
<p>We can also run <code>deno fmt main.ts</code> or use the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno">Deno extension for VSCode</a> to automatically format the source code in this file.</p>
<h2 id="step-2-define-the-base-url-for-the-hacker-news-api">Step 2: Define the base URL for the Hacker News API</h2>
<p>Since we will interact with the <a target="_blank" href="https://github.com/HackerNews/API">Hacker News API</a>, let's clear the <code>main.ts</code> file and define the <code>baseURL</code> variable as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">/** Base URL for all calls to the Hacker News API */</span>
<span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">"https://hacker-news.firebaseio.com/v0"</span>;
</code></pre>
<p>This URL is the target for the HTTP requests we will make in later steps.</p>
<h2 id="step-3-define-the-item-interface">Step 3: Define the <code>Item</code> interface</h2>
<p>The Hacker News API represents user-generated content, including comments, as <a target="_blank" href="https://github.com/HackerNews/API#items">items</a> with various properties. We can identify items using the properties <code>id</code>, a unique incrementing integer number, and <code>type</code>, an enumeration of different item types.</p>
<p>Following the <a target="_blank" href="https://github.com/HackerNews/API#items">official API specification</a>, let's model an item using a <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/2/objects.html">TypeScript interface</a>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">"https://hacker-news.firebaseio.com/v0"</span>;

<span class="hljs-comment">/** Item represents an item fetched from the HN API */</span>
<span class="hljs-keyword">interface</span> Item {
  <span class="hljs-comment">/** Unique item ID; the only required property */</span>
  <span class="hljs-keyword">readonly</span> id: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** Item type (`job`, `story`, `comment`, `poll`, or `pollopt`) */</span>
  <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">type</span>?: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">/** Username of the user who submitted the item */</span>
  <span class="hljs-keyword">readonly</span> by?: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">/** Title text for a story, poll or job */</span>
  <span class="hljs-keyword">readonly</span> title?: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">/** URL for a story or job */</span>
  <span class="hljs-keyword">readonly</span> url?: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">/** Text for a story, comment, poll, poll option or job */</span>
  <span class="hljs-keyword">readonly</span> text?: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">/** Unix timestamp for when the item was created */</span>
  <span class="hljs-keyword">readonly</span> time?: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** Score for a story, job or poll; votes for a poll option */</span>
  <span class="hljs-keyword">readonly</span> score?: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** Number of total comments for a story or poll */</span>
  <span class="hljs-keyword">readonly</span> descendants?: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** Set to `true` if the item is deleted */</span>
  <span class="hljs-keyword">readonly</span> deleted?: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-comment">/** Set to `true` if the item is dead */</span>
  <span class="hljs-keyword">readonly</span> dead?: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-comment">/** ID of the parent item of a comment (a story or another comment) */</span>
  <span class="hljs-keyword">readonly</span> parent?: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** List of IDs of the item's comments, in display order */</span>
  <span class="hljs-keyword">readonly</span> kids?: <span class="hljs-built_in">number</span>[];
  <span class="hljs-comment">/** ID of the poll associated to a poll option */</span>
  <span class="hljs-keyword">readonly</span> poll?: <span class="hljs-built_in">number</span>;
  <span class="hljs-comment">/** List of IDs of related poll options, in display order */</span>
  <span class="hljs-keyword">readonly</span> parts?: <span class="hljs-built_in">number</span>[];
}
</code></pre>
<p>Note that only the <code>id</code> property is required, all other properties are <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties">marked as optional</a> with <code>?</code>. All properties are also <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/2/objects.html#readonly-properties">marked as <code>readonly</code></a> so that they cannot be reassigned.</p>
<p>We won't be using all the properties present in <code>Item</code>, but they have been defined and documented for completeness.</p>
<h2 id="step-4-get-the-id-of-the-most-recent-item">Step 4: Get the ID of the most recent item</h2>
<p>Since item IDs are represented as an incrementing integer, the most recent item is the one with the largest ID. To get this value, we can use the <code>/maxitem.json</code> endpoint, that is <a target="_blank" href="https://hacker-news.firebaseio.com/v0/maxitem.json">https://hacker-news.firebaseio.com/v0/maxitem.json</a>.</p>
<p>Let's implement an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">asynchronous function</a> named <code>getMaxItemID()</code> that fetches this data:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">"https://hacker-news.firebaseio.com/v0"</span>;

<span class="hljs-keyword">interface</span> Item {
  <span class="hljs-comment">// Omitted for brevity</span>
}

<span class="hljs-comment">/** getMaxItemID returns the ID of the most recent item published on HN */</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMaxItemID</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">number</span>&gt; </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`<span class="hljs-subst">${baseURL}</span>/maxitem.json`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> id = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> id;
}
</code></pre>
<p>We first define the endpoint, then use the <code>fetch</code> web API <a target="_blank" href="https://deno.land/manual@v1.11.5/examples/fetch_data">implemented by Deno</a> to retrieve the JSON data representing the largest item ID.</p>
<p>As a quick test, let's also add this line below our function:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMaxItemID</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">number</span>&gt; </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`<span class="hljs-subst">${baseURL}</span>/maxitem.json`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> id = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> id;
}

<span class="hljs-comment">// Remove this line after this step</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">await</span> getMaxItemID());
</code></pre>
<p>Now let's run <code>deno run main.ts</code>:</p>
<pre><code class="lang-bash">$ deno run main.ts
error: Uncaught (<span class="hljs-keyword">in</span> promise) PermissionDenied: Requires net access to <span class="hljs-string">"hacker-news.firebaseio.com"</span>, run again with the --allow-net flag
  const res = await fetch(endpoint);
                    ^
    at deno:core/core.js:86:46
    at unwrapOpResult (deno:core/core.js:106:13)
    at Object.opSync (deno:core/core.js:120:12)
    at opFetch (deno:extensions/fetch/26_fetch.js:43:17)
    at mainFetch (deno:extensions/fetch/26_fetch.js:170:61)
    at deno:extensions/fetch/26_fetch.js:395:7
    at new Promise (&lt;anonymous&gt;)
    at fetch (deno:extensions/fetch/26_fetch.js:357:15)
    at getMaxItemID (file:///home/&lt;your_username&gt;/&lt;some_directory&gt;/main.ts:43:21)
    at file:///home/&lt;your_username&gt;/&lt;some_directory&gt;/main.ts:48:1
</code></pre>
<p>Since Deno is secure by default, it prevented our script from accessing the network connection to fetch data from <code>hacker-news.firebaseio.com</code>. As explained in the error message, we need to use the <a target="_blank" href="https://deno.land/manual@v1.11.5/getting_started/permissions#network-access"><code>--allow-net</code> flag</a> to explicitly grant this permission when running the script:</p>
<pre><code class="lang-bash">$ deno run --allow-net main.ts
27121843
</code></pre>
<p>Now that the script can reach the API, we can see the ID of the most recent item being printed in the console.</p>
<p>Before going to the next step, let's remove the line <code>console.log(await getMaxItemID());</code> that we just added.</p>
<h2 id="step-5-get-the-most-recent-item-by-its-id">Step 5: Get the most recent item by its ID</h2>
<p>Now that we have the ID of the most recent item, we can get the item itself by using the <code>/item/&lt;id&gt;.json</code> endpoint, for example <a target="_blank" href="https://hacker-news.firebaseio.com/v0/item/27121843.json">https://hacker-news.firebaseio.com/v0/item/27121843.json</a>.</p>
<p>Let's implement another asynchronous function named <code>getItemByID()</code> that fetches an item given its ID:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-comment">/** getItemByID fetches an item from the HN API given its ID */</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getItemByID</span>(<span class="hljs-params">id: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Item</span> | <span class="hljs-title">undefined</span>&gt; </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`<span class="hljs-subst">${baseURL}</span>/item/<span class="hljs-subst">${id}</span>.json`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> item ?? <span class="hljs-literal">undefined</span>;
}
</code></pre>
<p>If the ID is valid, <code>getItemByID()</code> will return an object described by the <code>Item</code> interface; otherwise, it will return <code>undefined</code>. The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator">nullish coalescing operator</a> (<code>??</code>) is supported out of the box by Deno.</p>
<p>We can now fetch and print the latest item by adding the line <code>console.log(await getItemByID(await getMaxItemID()));</code> as shown:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getItemByID</span>(<span class="hljs-params">id: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Item</span> | <span class="hljs-title">undefined</span>&gt; </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`<span class="hljs-subst">${baseURL}</span>/item/<span class="hljs-subst">${id}</span>.json`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-keyword">return</span> item ?? <span class="hljs-literal">undefined</span>;
}

<span class="hljs-comment">// Remove this line after this step</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">await</span> getItemByID(<span class="hljs-keyword">await</span> getMaxItemID()));
</code></pre>
<p>Running again our script with <code>deno run --allow-net main.ts</code> should display an item similar to this:</p>
<pre><code class="lang-bash">$ deno run --allow-net main.ts
Check file:///home/&lt;your_username&gt;/&lt;some_directory&gt;/main.ts
{
  by: <span class="hljs-string">"velut"</span>,
  id: 27121843,
  parent: 27121265,
  text: <span class="hljs-string">"Thank you!&lt;p&gt;Yes, I use prism-react-renderer for syntax highlighting.&lt;p&gt;I did not know about Shiki, ..."</span>,
  time: 1620758446,
  <span class="hljs-built_in">type</span>: <span class="hljs-string">"comment"</span>
}
</code></pre>
<p>Note that the item displayed in your terminal will be different and won't necessarily be a comment. Sometimes it may also be <code>undefined</code> if the HN API has not properly updated yet internally.</p>
<h2 id="step-6-stream-comments-from-hacker-news">Step 6: Stream comments from Hacker News</h2>
<p>Now that we have the necessary building blocks, let's implement the <code>streamComments()</code> function.</p>
<p>First, let's get the ID of the most recent item:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-comment">/** streamComments continuously fetches and displays the most recent comments published on HN */</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">streamComments</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Get the ID of the most recent item</span>
  <span class="hljs-keyword">let</span> id = <span class="hljs-keyword">await</span> getMaxItemID();
}
</code></pre>
<p>Then, get the item, increment the ID for the next item and loop forever:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">streamComments</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> id = <span class="hljs-keyword">await</span> getMaxItemID();

  <span class="hljs-comment">// Keep running forever</span>
  <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
    <span class="hljs-comment">// Get the item</span>
    <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> getItemByID(id);

    <span class="hljs-comment">// Increment the ID for the next item</span>
    id += <span class="hljs-number">1</span>;
  }
}
</code></pre>
<p>However, we need to wait a little if an item still does not exist. To do so, let's <a target="_blank" href="https://deno.land/manual@v1.11.5/examples/import_export#remote-import">import a remote module</a> containing a <code>sleep</code> function:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// Import the `sleep` function from a remote module</span>
<span class="hljs-keyword">import</span> { sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">"https://deno.land/x/sleep/mod.ts"</span>;

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">streamComments</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> id = <span class="hljs-keyword">await</span> getMaxItemID();

  <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
    <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> getItemByID(id);

    <span class="hljs-comment">// If the item does not exist, wait 5 seconds and try again</span>
    <span class="hljs-keyword">if</span> (!item) {
      <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">5</span>);
      <span class="hljs-keyword">continue</span>;
    }

    id += <span class="hljs-number">1</span>;
  }
}
</code></pre>
<p>To import modules in our Deno scripts, we use file paths for local modules (for example, <code>import ... from "./some/local/module.ts"</code>) and URLs for remote modules (for example, <code>import ... from "https://example.com/some/remote/module.ts</code>).</p>
<p>We don't need to install packages from a centralized remote repository like <a target="_blank" href="https://www.npmjs.com/">npm</a>, Deno will instead download and cache the remote modules and their dependencies the first time they are used.</p>
<p>Let's now complete the <code>streamComments()</code> function by printing only comments:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-keyword">import</span> { sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">"https://deno.land/x/sleep/mod.ts"</span>;

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">streamComments</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> id = <span class="hljs-keyword">await</span> getMaxItemID();

  <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
    <span class="hljs-keyword">const</span> item = <span class="hljs-keyword">await</span> getItemByID(id);
    <span class="hljs-keyword">if</span> (!item) {
      <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">5</span>);
      <span class="hljs-keyword">continue</span>;
    }

    <span class="hljs-comment">// Print only items that are visible comments</span>
    <span class="hljs-keyword">const</span> { <span class="hljs-keyword">type</span>, deleted, dead } = item;
    <span class="hljs-keyword">const</span> removed = deleted || dead || <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">type</span> === <span class="hljs-string">"comment"</span> &amp;&amp; !removed) {
      <span class="hljs-comment">// Get the comment's author, if any</span>
      <span class="hljs-keyword">const</span> author = item.by ?? <span class="hljs-string">"unknown author"</span>;

      <span class="hljs-comment">// Get the comment's URL on HN website</span>
      <span class="hljs-keyword">const</span> hnURL = <span class="hljs-string">`https://news.ycombinator.com/item?id=<span class="hljs-subst">${id}</span>`</span>;

      <span class="hljs-comment">// Print the comment</span>
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`\nRead new comment by <span class="hljs-subst">${author}</span> on <span class="hljs-subst">${hnURL}</span>`</span>);
      <span class="hljs-built_in">console</span>.log(item);
    }

    id += <span class="hljs-number">1</span>;
  }
}
</code></pre>
<p>Let's finish our script by calling <code>await streamComments()</code> at the bottom:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">streamComments</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">await</span> streamComments();
</code></pre>
<p>You can compare your script with <a target="_blank" href="https://gist.github.com/velut/6d0335c3ede885e11ce7c5768107d103">the final version of the script</a>.</p>
<h2 id="step-7-running-the-script">Step 7: Running the script</h2>
<p>Now that our script is complete, we can run it with the same <code>deno run --allow-net main.ts</code> command used before:</p>
<pre><code class="lang-bash">$ deno run --allow-net main.ts
Download https://deno.land/x/sleep/mod.ts
Warning Implicitly using latest version (v1.2.0) <span class="hljs-keyword">for</span> https://deno.land/x/sleep/mod.ts
Download https://deno.land/x/sleep@v1.2.0/mod.ts
Download https://deno.land/x/sleep@v1.2.0/sleep.ts
Download https://deno.land/std/<span class="hljs-built_in">log</span>/mod.ts
&lt;...many other downloads...&gt;
Check file:///home/&lt;your_username&gt;/&lt;some_directory&gt;/main.ts

Read new comment by tonyedgecombe on https://news.ycombinator.com/item?id=27762774
{
  by: <span class="hljs-string">"tonyedgecombe"</span>,
  id: 27762774,
  parent: 27761864,
  text: <span class="hljs-string">"&amp;gt;The printer stack which has barely received an update since Windows XP is a perfect example.&lt;p&gt;T..."</span>,
  time: 1625674491,
  <span class="hljs-built_in">type</span>: <span class="hljs-string">"comment"</span>
}

Read new comment by jph on https://news.ycombinator.com/item?id=27762775
{
  by: <span class="hljs-string">"jph"</span>,
  id: 27762775,
  parent: 27762699,
  text: <span class="hljs-string">"The catchphrase is &amp;quot;neurons that fire together wire together&amp;quot;."</span>,
  time: 1625674509,
  <span class="hljs-built_in">type</span>: <span class="hljs-string">"comment"</span>
}

Read new comment by exo-pla-net on https://news.ycombinator.com/item?id=27762777
{
  by: <span class="hljs-string">"exo-pla-net"</span>,
  id: 27762777,
  parent: 27762326,
  text: <span class="hljs-string">"The plant kingdom is almost entirely mono-food-sourced. They almost all rely on the sun, which, pend..."</span>,
  time: 1625674519,
  <span class="hljs-built_in">type</span>: <span class="hljs-string">"comment"</span>
}
</code></pre>
<p>As we can see, Deno first downloads the required remote modules and then starts running our script. After a few seconds, we should be able to read the stream of comments being published to Hacker News in real time.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this post, we discovered Deno, a simple, modern and secure alternative to Node.js, and explored many of its built-in features such as TypeScript support, web APIs, ES modules imports and modern Javascript syntax. These features allowed us to quickly implement and run a small script that fetches data in real time from a third party API.</p>
<p>You can learn more about Deno from its <a target="_blank" href="https://deno.land/">website</a> and <a target="_blank" href="https://deno.land/manual">manual</a>. When developing more complex scripts, you may also want to use Deno's <a target="_blank" href="https://deno.land/std@0.100.0">standard library</a> or search for <a target="_blank" href="https://deno.land/x">third party modules</a>.</p>
<p>If you liked this article and want to know when I post more, you can <a target="_blank" href="https://twitter.com/EdoardoScibona">follow me on Twitter</a>.</p>
<h2 id="credits">Credits</h2>
<ul>
<li>Cover photo by <a href="https://unsplash.com/@katie_s?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Katie Smith</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Exploring the npm registry API]]></title><description><![CDATA[In this post, we will learn how to use the REST API provided by the  npm registry to programmatically discover public Javascript packages and retrieve their metadata.
What is npm?
The term npm refers both to:

The npm CLI tool installed by default wi...]]></description><link>https://www.edoardoscibona.com/exploring-the-npm-registry-api</link><guid isPermaLink="true">https://www.edoardoscibona.com/exploring-the-npm-registry-api</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[npm]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Edoardo Scibona]]></dc:creator><pubDate>Fri, 02 Jul 2021 14:44:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625154770880/5X-_SC2b3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, we will learn how to use the REST API provided by the  <a target="_blank" href="https://www.npmjs.com/">npm registry</a> to programmatically discover public Javascript packages and retrieve their metadata.</p>
<h2 id="what-is-npm">What is npm?</h2>
<p>The term <code>npm</code> refers both to:</p>
<ul>
<li>The <code>npm</code> CLI tool installed by default with <a target="_blank" href="https://nodejs.org/">Node.js</a> on your machine</li>
<li>The <a target="_blank" href="https://www.npmjs.com/">npm registry</a>, an online service which collects more than 1.6M public Javascript packages</li>
</ul>
<p>For example, when running the <code>npm install react</code> command in your Javascript project, you are downloading the <code>react</code> package from the online npm registry.</p>
<p>In this post, we are interested in the online service and its public API.</p>
<h2 id="what-is-the-npm-registry-api">What is the npm registry API?</h2>
<p>While many people regularly use <a target="_blank" href="https://www.npmjs.com/">npm's website</a> to discover packages, only a few know that npm also provides a <a target="_blank" href="https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md">public REST API</a> accessible at <a target="_blank" href="https://registry.npmjs.org/">registry.npmjs.org</a>.</p>
<p>This API provides methods to:</p>
<ul>
<li>Get information about the registry itself</li>
<li>Get all available information about a specific package</li>
<li>Get information about a specific version of a package</li>
<li>Search packages by text</li>
<li>Count the number of downloads for packages</li>
</ul>
<p>We can call these methods by:</p>
<ul>
<li>Sending HTTP requests to the correct endpoints</li>
<li>Using a <a target="_blank" href="https://www.jsdocs.io/package/query-registry">fully typed</a> package I developed named <a target="_blank" href="https://www.npmjs.com/package/query-registry">query-registry</a></li>
</ul>
<h2 id="before-we-start">Before we start</h2>
<p>You can follow along online on <a target="_blank" href="https://runkit.com/velut/60dde43a820278001aacda44">this RunKit notebook</a> or on your machine by installing the <code>isomorphic-unfetch</code> and <code>query-registry</code> packages as follows:</p>
<pre><code class="lang-bash">npm install isomorphic-unfetch query-registry
</code></pre>
<p>You can also use as references the <a target="_blank" href="https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md">official API specification</a> and the <a target="_blank" href="https://www.jsdocs.io/package/query-registry">documentation for query-registry</a>.</p>
<p>Finally, you can explore the API and its responses in your browser by going to <a target="_blank" href="https://registry.npmjs.org/">registry.npmjs.org</a>.</p>
<h2 id="example-1-get-information-about-the-registry-itself">Example 1: Get information about the registry itself</h2>
<h3 id="endpoint">Endpoint</h3>
<p>If we want to know more about the underlying database used by the registry, we can send a <code>GET</code> request to the <code>/</code> endpoint, that is <a target="_blank" href="https://registry.npmjs.org/"><code>https://registry.npmjs.org/</code></a>.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example1WithFetch</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">"https://registry.npmjs.org/"</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example1WithQueryRegistry</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.getRegistryMetadata();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>We receive a response containing information about the registry's database, including its name and some interesting attributes, as shown below:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"db_name"</span>:<span class="hljs-string">"registry"</span>,
  <span class="hljs-attr">"engine"</span>:<span class="hljs-string">"couch_bt_engine"</span>,
  <span class="hljs-attr">"doc_count"</span>:<span class="hljs-number">2226548</span>,
  <span class="hljs-attr">"doc_del_count"</span>:<span class="hljs-number">334</span>,
  <span class="hljs-attr">"update_seq"</span>:<span class="hljs-number">5769731</span>,
  <span class="hljs-attr">"purge_seq"</span>:<span class="hljs-number">0</span>,
  <span class="hljs-attr">"compact_running"</span>:<span class="hljs-literal">false</span>,
  <span class="hljs-attr">"sizes"</span>:{
    <span class="hljs-attr">"active"</span>:<span class="hljs-number">57693928578</span>,
    <span class="hljs-attr">"external"</span>:<span class="hljs-number">132154863659</span>,
    <span class="hljs-attr">"file"</span>:<span class="hljs-number">58937123056</span>
  },
  <span class="hljs-attr">"disk_size"</span>:<span class="hljs-number">58937123056</span>,
  <span class="hljs-attr">"data_size"</span>:<span class="hljs-number">57693928578</span>,
  <span class="hljs-attr">"other"</span>:{
    <span class="hljs-attr">"data_size"</span>:<span class="hljs-number">132154863659</span>
  },
  <span class="hljs-attr">"instance_start_time"</span>:<span class="hljs-string">"1624686290809498"</span>,
  <span class="hljs-attr">"disk_format_version"</span>:<span class="hljs-number">7</span>,
  <span class="hljs-attr">"committed_update_seq"</span>:<span class="hljs-number">5769731</span>,
  <span class="hljs-attr">"compacted_seq"</span>:<span class="hljs-number">5729968</span>,
  <span class="hljs-attr">"uuid"</span>:<span class="hljs-string">"964c127ddcbbd59982db296a0f9e8a56"</span>
}
</code></pre>
<h2 id="example-2-get-all-available-package-metadata">Example 2: Get all available package metadata</h2>
<h3 id="endpoint">Endpoint</h3>
<p>If we want to get a <em>packument</em> (package document) containing all the information available on a package, we can send a <code>GET</code> request to the <code>/&lt;package&gt;</code> endpoint, for example <a target="_blank" href="https://registry.npmjs.org/react">https://registry.npmjs.org/react</a> or <a target="_blank" href="https://registry.npmjs.org/@types/node">https://registry.npmjs.org/@types/node</a>.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example2WithFetch</span>(<span class="hljs-params">name</span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://registry.npmjs.org/<span class="hljs-subst">${name}</span>`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example2WithQueryRegistry</span>(<span class="hljs-params">name</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.getPackument({ name });
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>We receive a response containing all the data associated to a package, including its ID, name, description, author, license and manifests for each published version.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"_id"</span>: <span class="hljs-string">"react"</span>,
  <span class="hljs-attr">"_rev"</span>: <span class="hljs-string">"1684-29eba7dd741dee3c165b86b7e4f63461"</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"react"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"React is a JavaScript library for building user interfaces."</span>,
  <span class="hljs-attr">"dist-tags"</span>: {…},
  <span class="hljs-attr">"versions"</span>: {…},
  <span class="hljs-attr">"maintainers"</span>: […],
  <span class="hljs-attr">"time"</span>: {…},
  <span class="hljs-attr">"repository"</span>: {…},
  <span class="hljs-attr">"readme"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"readmeFilename"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://reactjs.org/"</span>,
  <span class="hljs-attr">"keywords"</span>: […],
  <span class="hljs-attr">"bugs"</span>: {…},
  <span class="hljs-attr">"users"</span>: {…},
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>
}
</code></pre>
<h2 id="example-3-get-information-about-a-specific-version-of-a-package">Example 3: Get information about a specific version of a package</h2>
<h3 id="endpoint">Endpoint</h3>
<p>If we want to get a <em>package manifest</em> containing information about a specific version of a package, for example <code>react@17.0.2</code> or <code>@types/node@15.14.0</code>, we can send a <code>GET</code> request to the <code>/&lt;package&gt;/&lt;version&gt;</code> endpoint, for example <a target="_blank" href="https://registry.npmjs.org/react/17.0.2">https://registry.npmjs.org/react/17.0.2</a> or <a target="_blank" href="https://registry.npmjs.org/@types/node/15.14.0">https://registry.npmjs.org/@types/node/15.14.0</a>.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example3WithFetch</span>(<span class="hljs-params">name, version</span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://registry.npmjs.org/<span class="hljs-subst">${name}</span>/<span class="hljs-subst">${version}</span>`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example3WithQueryRegistry</span>(<span class="hljs-params">name, version</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.getPackageManifest({ name, version });
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>We receive a response containing data that describes a published version of a package.
This data consists of the contents of <code>package.json</code> at publishing time plus some additional attributes added by the registry.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"react"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"React is a JavaScript library for building user interfaces."</span>,
  <span class="hljs-attr">"keywords"</span>: […],
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"17.0.2"</span>,
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://reactjs.org/"</span>,
  <span class="hljs-attr">"bugs"</span>: {…},
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"repository"</span>: {…},
  <span class="hljs-attr">"engines"</span>: {…},
  <span class="hljs-attr">"dependencies"</span>: {…},
  <span class="hljs-attr">"browserify"</span>: {…},
  <span class="hljs-attr">"_id"</span>: <span class="hljs-string">"react@17.0.2"</span>,
  <span class="hljs-attr">"_nodeVersion"</span>: <span class="hljs-string">"15.11.0"</span>,
  <span class="hljs-attr">"_npmVersion"</span>: <span class="hljs-string">"7.6.0"</span>,
  <span class="hljs-attr">"dist"</span>: {…},
  <span class="hljs-attr">"_npmUser"</span>: {…},
  <span class="hljs-attr">"directories"</span>: {},
  <span class="hljs-attr">"maintainers"</span>: […],
  <span class="hljs-attr">"_npmOperationalInternal"</span>: {…},
  <span class="hljs-attr">"_hasShrinkwrap"</span>: <span class="hljs-literal">false</span>,
}
</code></pre>
<h2 id="example-4-search-packages-by-text">Example 4: Search packages by text</h2>
<h3 id="endpoint">Endpoint</h3>
<p>If we want to search packages by text, we can send a <code>GET</code> request to the <code>/-/v1/search?text=&lt;some query&gt;</code> endpoint, for example <a target="_blank" href="https://registry.npmjs.org/-/v1/search?text=react">https://registry.npmjs.org/-/v1/search?text=react</a>.</p>
<p>We can also use special keyword parameters in our text query to improve our results. For example, to find packages that I published we can use the <code>author:velut</code> keyword parameter like this: <a target="_blank" href="https://registry.npmjs.org/-/v1/search?text=author:velut">https://registry.npmjs.org/-/v1/search?text=author:velut</a>.</p>
<p>The <a target="_blank" href="https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#get-v1search">official API specification</a> contains the full list of supported search criteria.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example4WithFetch</span>(<span class="hljs-params">text</span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://registry.npmjs.org/-/v1/search?text=<span class="hljs-subst">${text}</span>`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example4WithQueryRegistry</span>(<span class="hljs-params">text</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.searchPackages({ <span class="hljs-attr">query</span>: { text } });
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>We receive a response containing a list of packages matching our query inside the <code>objects</code> attribute. Each package comes with a small number of important attributes, including <code>name</code> and <code>version</code>, plus some score values for the package itself and for its relevance to our query.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"objects"</span>: [
    {
      <span class="hljs-attr">"package"</span>: {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"react"</span>,
        <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"unscoped"</span>,
        <span class="hljs-attr">"version"</span>: <span class="hljs-string">"17.0.2"</span>,
        <span class="hljs-attr">"description"</span>: <span class="hljs-string">"React is a JavaScript library for building user interfaces."</span>,
        <span class="hljs-attr">"keywords"</span>: [<span class="hljs-string">"react"</span>],
        <span class="hljs-attr">"date"</span>: <span class="hljs-string">"2021-03-22T21:56:19.536Z"</span>,
        <span class="hljs-attr">"links"</span>: {
          <span class="hljs-attr">"npm"</span>: <span class="hljs-string">"https://www.npmjs.com/package/react"</span>,
          <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://reactjs.org/"</span>,
          <span class="hljs-attr">"repository"</span>: <span class="hljs-string">"https://github.com/facebook/react"</span>,
          <span class="hljs-attr">"bugs"</span>: <span class="hljs-string">"https://github.com/facebook/react/issues"</span>
        },
        <span class="hljs-attr">"publisher"</span>: {
          <span class="hljs-attr">"username"</span>: <span class="hljs-string">"…"</span>,
          <span class="hljs-attr">"email"</span>: <span class="hljs-string">"…"</span>
        },
        <span class="hljs-attr">"maintainers"</span>: [
          { <span class="hljs-attr">"username"</span>: <span class="hljs-string">"…"</span>, <span class="hljs-attr">"email"</span>: <span class="hljs-string">"…"</span> },
          { <span class="hljs-attr">"username"</span>: <span class="hljs-string">"…"</span>, <span class="hljs-attr">"email"</span>: <span class="hljs-string">"…"</span> }
        ]
      },
      <span class="hljs-attr">"score"</span>: {
        <span class="hljs-attr">"final"</span>: <span class="hljs-number">0.5866665170132767</span>,
        <span class="hljs-attr">"detail"</span>: {
          <span class="hljs-attr">"quality"</span>: <span class="hljs-number">0.5246016720020373</span>,
          <span class="hljs-attr">"popularity"</span>: <span class="hljs-number">0.8931981392742823</span>,
          <span class="hljs-attr">"maintenance"</span>: <span class="hljs-number">0.3333333333333333</span>
        }
      },
      <span class="hljs-attr">"searchScore"</span>: <span class="hljs-number">100000.63</span>
    }
  ],
  <span class="hljs-attr">"total"</span>: <span class="hljs-number">164637</span>,
  <span class="hljs-attr">"time"</span>: <span class="hljs-string">"Fri Jul 02 2021 13:13:14 GMT+0000 (Coordinated Universal Time)"</span>
}
</code></pre>
<h2 id="example-5-count-downloads-for-a-package">Example 5: Count downloads for a package</h2>
<h3 id="endpoint">Endpoint</h3>
<p>If we want to count the number of downloads for a package in a given time period, we can send a <code>GET</code> request to a slightly different API endpoint at <code>https://api.npmjs.org/downloads/point/&lt;period&gt;/&lt;package&gt;</code>, for example <a target="_blank" href="https://api.npmjs.org/downloads/point/last-week/react">https://api.npmjs.org/downloads/point/last-week/react</a>. Supported time periods include <code>last-day</code>, <code>last-week</code>, <code>last-month</code> and <code>last-year</code>.</p>
<p>The <a target="_blank" href="https://github.com/npm/registry/blob/master/docs/download-counts.md">download counts API</a> also provides other methods to count downloads for packages and for the whole registry.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example5WithFetch</span>(<span class="hljs-params">name, period</span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://api.npmjs.org/downloads/point/<span class="hljs-subst">${period}</span>/<span class="hljs-subst">${name}</span>`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example5WithQueryRegistry</span>(<span class="hljs-params">name, period</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.getPackageDownloads({ name, period });
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>We receive a simple response containing the package's name, its total number of downloads and the start and end dates for the selected time period.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"downloads"</span>: <span class="hljs-number">10889040</span>,
  <span class="hljs-attr">"start"</span>: <span class="hljs-string">"2021-06-25"</span>,
  <span class="hljs-attr">"end"</span>: <span class="hljs-string">"2021-07-01"</span>,
  <span class="hljs-attr">"package"</span>: <span class="hljs-string">"react"</span>
}
</code></pre>
<h2 id="bonus-using-a-registry-mirror">Bonus: Using a registry mirror</h2>
<h3 id="why-use-a-mirror">Why use a mirror?</h3>
<p>Sometimes we may want to use a proxy or mirror of the npm registry instead of the original registry itself. For example, <a target="_blank" href="https://www.cloudflare.com/">Cloudflare</a> provides a mirror at <a target="_blank" href="https://registry.npmjs.cf/">https://registry.npmjs.cf</a> with <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> enabled, allowing us to query the registry directly from the browser or client-side applications.</p>
<p>For example, try pasting this snippet in your browser's console:</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"https://registry.npmjs.org/react"</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()).then(<span class="hljs-built_in">console</span>.log)
</code></pre>
<p>It should fail with a CORS error because it's using the original registry. Instead, the following snippet should work because it's using Cloudflare's registry mirror.</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"https://registry.npmjs.cf/react"</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()).then(<span class="hljs-built_in">console</span>.log)
</code></pre>
<h3 id="endpoint">Endpoint</h3>
<p>We can use the same endpoints available on <a target="_blank" href="https://registry.npmjs.org/">registry.npmjs.org</a> provided that they are supported by the chosen mirror registry.</p>
<h3 id="with-fetch">With <code>fetch</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bonusWithFetch</span>(<span class="hljs-params">name</span>) </span>{
  <span class="hljs-keyword">const</span> endpoint = <span class="hljs-string">`https://registry.npmjs.cf/<span class="hljs-subst">${name}</span>`</span>;
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(endpoint);
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="with-query-registry">With <code>query-registry</code></h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bonusWithQueryRegistry</span>(<span class="hljs-params">name, registry</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> queryRegistry.getPackument({ name, registry });
  <span class="hljs-built_in">console</span>.log(data);
}
</code></pre>
<h3 id="response">Response</h3>
<p>The responses should be the same as the ones provided by the original npm registry, maybe slightly delayed due to the mirroring process.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this post, we have learnt what is npm, how we can use its public API to discover and analyze public Javascript packages and how we can take advantage of API client wrappers such as <a target="_blank" href="https://www.npmjs.com/package/query-registry">query-registry</a> and registry mirrors such as <a target="_blank" href="https://registry.npmjs.cf/">https://registry.npmjs.cf</a> to improve our interactions with this API both in server-side and client-side Javascript applications.</p>
<p>Be sure to leave a comment if you have any doubts or if you end up building something interesting using this lesser known but powerful API.</p>
<p>If you liked this article and want to know when I post more, you can <a target="_blank" href="https://twitter.com/EdoardoScibona">follow me on Twitter</a>.</p>
<h2 id="credits">Credits</h2>
<ul>
<li>Cover photo by <a href="https://unsplash.com/@ripato?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ricardo Gomez Angel</a> on <a href="https://unsplash.com/collections/3743779/ricardo-gomez-angel?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[Photo by Drew Beamer on Unsplash]]></description><link>https://www.edoardoscibona.com/hello-world</link><guid isPermaLink="true">https://www.edoardoscibona.com/hello-world</guid><category><![CDATA[first post]]></category><dc:creator><![CDATA[Edoardo Scibona]]></dc:creator><pubDate>Thu, 24 Jun 2021 23:54:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1624578691992/jqeeYoA7T.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photo by <a href="https://unsplash.com/@drew_beamer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Drew Beamer</a> on <a href="https://unsplash.com/s/photos/hello?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item></channel></rss>