<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://vladimirgorej.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://vladimirgorej.com/" rel="alternate" type="text/html" /><updated>2026-10-15T11:10:00+00:00</updated><id>https://vladimirgorej.com/feed.xml</id><title type="html">Vladimír Gorej’s Site</title><subtitle>Vladimír Gorej is a Prague-based Senior Full-Stack Engineer building open-source tooling for OpenAPI, AsyncAPI, Arazzo, and JSON Schema.</subtitle><author><name>Vladimír Gorej</name></author><entry><title type="html">From packets to pixels: how the internet actually works</title><link href="https://vladimirgorej.com/blog/from-packets-to-pixels-how-the-internet-actually-works/" rel="alternate" type="text/html" title="From packets to pixels: how the internet actually works" /><published>2026-08-05T08:00:00+00:00</published><updated>2026-08-05T08:00:00+00:00</updated><id>https://vladimirgorej.com/blog/from-packets-to-pixels-how-the-internet-actually-works</id><content type="html" xml:base="https://vladimirgorej.com/blog/from-packets-to-pixels-how-the-internet-actually-works/"><![CDATA[<aside class="series-cta">
  <div class="series-cta__kicker">From packets to pixels &middot; Part 1</div>
  <p class="series-cta__text">
    This article is part of a hands-on series that rebuilds the web from first principles:
    the network first, then HTML, CSS, and JavaScript.
  </p>
  <div class="d-flex flex-wrap gap-2">
    <a class="btn text-white series-cta__btn" href="/from-packets-to-pixels/">
      All parts <i class="fa-solid fa-arrow-right"></i>
    </a>
    <button type="button" class="btn series-cta__subscribe-btn" data-bs-toggle="modal" data-bs-target="#seriesSubscribeModal">
      <i class="fa-solid fa-envelope"></i> Get new parts by email
    </button>
  </div>
</aside>

<p class="lead">
  I have spent my whole career close to the web's machinery: five years maintaining Swagger tools, these days
  <a href="https://speclynx.com" target="_blank" rel="noopener noreferrer">SpecLynx</a>,
  <a href="https://usearazzo.com" target="_blank" rel="noopener noreferrer">UseArazzo</a> and
  <a href="https://github.com/swaggerexpert" target="_blank" rel="noopener noreferrer">SwaggerExpert</a>, plus
  daily work building tools that help agents talk to APIs. HTTP is where I live. And yet, when I recently
  stopped to examine my mental model of the internet itself, I realized how much of it I had never actually
  verified.
</p>

<p>Most of what an experienced engineer knows about the layers beneath their own code is inherited rather than
learned: absorbed from documentation skimmed at 2 a.m., from a colleague’s offhand explanation, from a bug
that got fixed without ever being fully understood. That model works. It’s mostly right. But “mostly right”
stays invisible until the day a <abbr title="Domain Name System">DNS</abbr> change stubbornly refuses to take effect, or a retried request duplicates
an order, and you discover the gap had been sitting there the whole time.</p>

<p>So I went back and checked. Not the exotic corners: the basics. What a packet actually is. Why a bare domain
traditionally can’t be a <code class="language-plaintext highlighter-rouge">CNAME</code>. What “<abbr title="Domain Name System">DNS</abbr> propagation” really means (nothing propagates anywhere). Where a
browser spends its time between receiving bytes and showing you pixels. Most of it confirmed what I already
believed. A few things corrected me.</p>

<p>This article is the write-up of that pass: the explanation I wish I’d been handed years ago, and the one I’d
now give to anyone who builds on the web without having looked underneath it. It walks the whole path once,
end to end: each section builds on the previous one, and each ends with hands-on exercises that need nothing
more than a terminal and a browser.</p>

<p>Run them. That part isn’t decoration. Verifying something yourself is the difference between knowing it and
having heard it, a distinction worth defending now that a confident, plausible explanation is always one
prompt away. A language model will happily tell you how <abbr title="Domain Name System">DNS</abbr> works. <code class="language-plaintext highlighter-rouge">dig +trace</code> will <em>show</em> you.</p>

<nav class="article-toc">
  <div class="article-toc__title">What's ahead</div>
  <ol>
    <li><a href="#a-network-of-networks">A network of networks: packets, IPs, and ports</a></li>
    <li><a href="#speaking-http">Speaking HTTP: requests, responses, and status codes</a></li>
    <li><a href="#domain-names">Domain names: leasing your corner of the namespace</a></li>
    <li><a href="#hosting">Hosting: where websites actually live</a></li>
    <li><a href="#dns">DNS: the lookup chain behind every click</a></li>
    <li><a href="#the-browser">The browser: from response bytes to rendered pixels</a></li>
  </ol>
</nav>

<h2 id="a-network-of-networks" class="article-section">A network of networks: packets, IPs, and ports</h2>

<p>The internet is not a single thing you connect to. It is a <em>network of networks</em>: your home network, your
<abbr title="Internet Service Provider">ISP</abbr>’s network, university networks, data-center networks, all agreeing to pass traffic between each other
using the same set of rules (protocols). Nobody owns or runs the whole thing.</p>

<p>So where do separate networks physically touch? Mostly at <strong>Internet Exchange Points (<abbr title="Internet Exchange Points">IXPs</abbr>)</strong>. An <abbr title="Internet Exchange Point">IXP</abbr> is,
quite literally, a building (or a few racks in a data center) containing a large shared network switch. <abbr title="Internet Service Providers">ISPs</abbr>,
cloud providers, and content companies each rent a port on that switch and agree to exchange traffic with each
other directly, an arrangement called <em>peering</em>. Without an <abbr title="Internet Exchange Point">IXP</abbr>, your <abbr title="Internet Service Provider">ISP</abbr> would have to pay intermediate
carriers to haul your video stream across the country and back; with one, both networks plug into the same
room and hand packets straight to each other. That keeps traffic local (a stream between two Prague networks
never needs to leave Prague), cuts costs, and shortens the path your packets travel. The largest exchanges,
like DE-CIX in Frankfurt or AMS-IX in Amsterdam, move tens of terabits per second at peak. Cooperation through
shared protocols and peering agreements, not central control, is what holds the internet together.</p>

<p>One distinction worth fixing early: <strong>the internet is not the web.</strong> The internet is the plumbing: cables,
routers, addresses, and the protocols that move data between machines. The <strong>web</strong> (websites, browsers, <abbr title="HyperText Markup Language">HTML</abbr>,
links) is just one application running on top of that plumbing, alongside email, video calls, online games,
and messaging apps. This article covers the plumbing first, then climbs up to the web.</p>

<p>When you open a website, two machines are involved:</p>

<ul>
  <li><strong>Client</strong>: the machine that asks for something (your laptop, your phone, a script).</li>
  <li><strong>Server</strong>: the machine that answers (a computer somewhere running software that listens for requests).</li>
</ul>

<p>“Client” and “server” describe <em>roles</em>, not hardware. The same machine can be a client in one exchange and a
server in another.</p>

<h3 id="ip-addresses-how-machines-find-each-other"><abbr title="Internet Protocol">IP</abbr> addresses: how machines find each other</h3>

<p>Every device directly reachable on the internet has an <strong><abbr title="Internet Protocol">IP</abbr> address</strong>, a numeric label that works like a
postal address for computers.</p>

<ul>
  <li><strong><abbr title="Internet Protocol version 4">IPv4</abbr></strong>: four numbers 0–255, e.g. <code class="language-plaintext highlighter-rouge">142.250.185.78</code>. Only about 4.3 billion possible addresses exist, and
we’ve effectively run out.</li>
  <li><strong><abbr title="Internet Protocol version 6">IPv6</abbr></strong>: much longer hex addresses, e.g. <code class="language-plaintext highlighter-rouge">2a00:1450:4001:82f::200e</code>. Designed to never run out.</li>
</ul>

<p>Because <abbr title="Internet Protocol version 4">IPv4</abbr> addresses are scarce, your home devices usually share one <em>public</em> <abbr title="Internet Protocol">IP</abbr> address. Your router hands
out <em>private</em> addresses (like <code class="language-plaintext highlighter-rouge">192.168.1.x</code>) internally and translates between the two, a trick called
<strong><abbr title="Network Address Translation">NAT</abbr></strong> (Network Address Translation).</p>

<h3 id="packets-how-data-actually-travels">Packets: how data actually travels</h3>

<p>Data is never sent as one continuous stream. It’s chopped into small chunks called <strong>packets</strong> (typically
~1,500 bytes). Each packet carries the destination <abbr title="Internet Protocol">IP</abbr>, the source <abbr title="Internet Protocol">IP</abbr>, a <strong>sequence number</strong>, and a slice of
the actual data. The sequence number records the chunk’s position in the original message: packet 1, packet 2,
packet 3, and so on. It’s what lets the receiving machine put the chunks back together in the right order, and
notice when one never arrived so it can be re-sent.</p>

<p>Packets travel independently. Two packets from the same message may take <em>different routes</em> through the
network and arrive out of order; the receiving machine uses the sequence numbers to reassemble them. This
design makes the internet resilient: if one route fails, packets simply flow around it. Between your machine
and a server, packets hop through a chain of <strong>routers</strong>, each forwarding the packet closer to its
destination.</p>

<h3 id="protocols-the-shared-rules">Protocols: the shared rules</h3>

<p>None of this works unless every machine speaks the same language. That’s what protocols are: agreed-upon
formats and procedures, stacked in layers:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Layer</th><th scope="col">Protocol</th><th scope="col">What it does</th></tr>
    </thead>
    <tbody>
      <tr><td>Addressing &amp; routing</td><td><strong><abbr title="Internet Protocol">IP</abbr></strong></td><td>Gets packets from one machine to another</td></tr>
      <tr><td>Transport</td><td><strong><abbr title="Transmission Control Protocol">TCP</abbr></strong></td><td>Reliable, ordered delivery; retransmits lost packets</td></tr>
      <tr><td>Transport</td><td><strong><abbr title="User Datagram Protocol">UDP</abbr></strong></td><td>Fast, no guarantees; good for video calls, games</td></tr>
      <tr><td>Application</td><td><strong><abbr title="HyperText Transfer Protocol">HTTP</abbr>, <abbr title="Simple Mail Transfer Protocol">SMTP</abbr>, <abbr title="Domain Name System">DNS</abbr>…</strong></td><td>Meaning of the data (web pages, email, name lookups)</td></tr>
    </tbody>
  </table>
</div>

<p><strong><abbr title="Transmission Control Protocol">TCP</abbr></strong> is worth understanding: before sending data, client and server perform a <em>handshake</em>
(<abbr title="synchronize">SYN</abbr> → <abbr title="synchronize">SYN</abbr>-<abbr title="acknowledge">ACK</abbr> → <abbr title="acknowledge">ACK</abbr>) to establish a connection. <abbr title="Transmission Control Protocol">TCP</abbr> then numbers every byte, acknowledges receipt, and
re-sends anything lost. The web runs mostly on <abbr title="Transmission Control Protocol">TCP</abbr> (though <abbr title="HyperText Transfer Protocol">HTTP</abbr>/3 now runs on <abbr title="a transport protocol built on UDP; originally &quot;Quick UDP Internet Connections&quot;">QUIC</abbr>, explained in the
<a href="#speaking-http"><abbr title="HyperText Transfer Protocol">HTTP</abbr> section</a>).</p>

<h3 id="ports-many-conversations-one-machine">Ports: many conversations, one machine</h3>

<p>A server has one <abbr title="Internet Protocol">IP</abbr> address but runs many services. <strong>Ports</strong> (numbers 0–65535) distinguish them, like
apartment numbers within one building:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Port</th><th scope="col">Service</th></tr>
    </thead>
    <tbody>
      <tr><td>80</td><td><abbr title="HyperText Transfer Protocol">HTTP</abbr> (web, unencrypted)</td></tr>
      <tr><td>443</td><td><abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr> (web, encrypted)</td></tr>
      <tr><td>22</td><td><abbr title="Secure Shell">SSH</abbr> (remote shell)</td></tr>
      <tr><td>25 / 587</td><td><abbr title="Simple Mail Transfer Protocol">SMTP</abbr> (email)</td></tr>
      <tr><td>53</td><td><abbr title="Domain Name System">DNS</abbr></td></tr>
    </tbody>
  </table>
</div>

<p>So a full “address” for a conversation is really <strong><abbr title="Internet Protocol">IP</abbr> + port</strong>: <code class="language-plaintext highlighter-rouge">142.250.185.78:443</code> means “the service
listening on port 443 at that machine.”</p>

<p>You already use this daily as a developer without leaving your desk. <strong><code class="language-plaintext highlighter-rouge">localhost</code></strong> is a reserved name
(resolving to <code class="language-plaintext highlighter-rouge">127.0.0.1</code>) that means “this very machine”: client and server both on your own computer. When
you run a dev server and open <code class="language-plaintext highlighter-rouge">http://localhost:3000</code>, your browser is the client, your project is the server,
and <code class="language-plaintext highlighter-rouge">3000</code> picks it out among everything else running locally. Dev tools grab high, unclaimed ports by
convention (<code class="language-plaintext highlighter-rouge">3000</code>, <code class="language-plaintext highlighter-rouge">5173</code>, <code class="language-plaintext highlighter-rouge">8000</code>, <code class="language-plaintext highlighter-rouge">8080</code>) because the low, well-known ones require admin rights and are
spoken for.</p>

<h3 id="the-whole-trip-previewed">The whole trip, previewed</h3>

<p>When you visit a website, roughly this happens; the rest of this article unpacks each step:</p>

<ol>
  <li>Your machine looks up the site’s <abbr title="Internet Protocol">IP</abbr> address (<a href="#dns"><abbr title="Domain Name System">DNS</abbr></a>).</li>
  <li>It opens a <abbr title="Transmission Control Protocol">TCP</abbr> connection to that <abbr title="Internet Protocol">IP</abbr> on port 443.</li>
  <li>Client and server negotiate encryption (<abbr title="Transport Layer Security">TLS</abbr>).</li>
  <li>Your browser sends an <a href="#speaking-http"><abbr title="HyperText Transfer Protocol">HTTP</abbr> request</a>.</li>
  <li>The server, wherever the site is <a href="#hosting">hosted</a>, answers; the response is split into packets and
routed hop by hop.</li>
  <li>Your <a href="#the-browser">browser</a> reassembles and renders it.</li>
</ol>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># What's my machine's local IP?</span>
ip addr        <span class="c"># Linux</span>
ipconfig       <span class="c"># Windows</span>

<span class="c"># What IP does a domain resolve to? How long is the round trip?</span>
ping example.com

<span class="c"># Watch your packets hop router by router</span>
traceroute example.com     <span class="c"># macOS / Linux</span>
tracert example.com        <span class="c"># Windows</span></code></pre></figure>

  <p>Then find your <em>public</em> <abbr title="Internet Protocol">IP</abbr> (search “what is my <abbr title="Internet Protocol">IP</abbr>”) and compare it to your local one. They differ; that’s
<abbr title="Network Address Translation">NAT</abbr> at work.</p>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>Why can two packets from the same download arrive out of order?</summary>

    <p>Because routers forward each packet independently, two packets may take different routes with different
delays. The receiver uses the sequence numbers to restore the original order.</p>

  </details>

  <details class="article-answer">
    <summary>What problem does <abbr title="Network Address Translation">NAT</abbr> solve, and what created that problem?</summary>

    <p><abbr title="Network Address Translation">NAT</abbr> lets all the devices in your home share a single public <abbr title="Internet Protocol">IP</abbr> address. The problem it works around is <abbr title="Internet Protocol version 4">IPv4</abbr>
address scarcity: the protocol’s design allows only about 4.3 billion addresses, far fewer than there are
devices in the world.</p>

  </details>

  <details class="article-answer">
    <summary>Your browser connects to <code>93.184.216.34:443</code>. What do the two parts mean?</summary>

    <p>The <abbr title="Internet Protocol">IP</abbr> address (<code class="language-plaintext highlighter-rouge">93.184.216.34</code>) identifies the machine; the port (<code class="language-plaintext highlighter-rouge">443</code>, the <abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr> port) identifies which
service on that machine the connection is for.</p>

  </details>

  <details class="article-answer">
    <summary>Why does a video call prefer <abbr title="User Datagram Protocol">UDP</abbr> while a file download prefers <abbr title="Transmission Control Protocol">TCP</abbr>?</summary>

    <p>A call values speed over completeness: a lost video frame is better skipped than retransmitted too late, so
<abbr title="User Datagram Protocol">UDP</abbr>’s “send and forget” fits. A download must be complete and correctly ordered down to the last byte, which
is exactly what <abbr title="Transmission Control Protocol">TCP</abbr> guarantees.</p>

  </details>

</details>

<h2 id="speaking-http" class="article-section">Speaking <abbr title="HyperText Transfer Protocol">HTTP</abbr>: requests, responses, and status codes</h2>

<p><strong><abbr title="HyperText Transfer Protocol">HTTP</abbr> (HyperText Transfer Protocol)</strong> is the application-level protocol browsers and servers use to exchange
web content. It’s a simple request-response conversation in (mostly) plain text: the client asks, the server
answers, done.</p>

<p><abbr title="HyperText Transfer Protocol">HTTP</abbr> is <strong>stateless</strong>: each request stands alone. The server doesn’t inherently remember you between
requests. Anything that <em>feels</em> stateful (being logged in, a shopping cart) is built on top, usually with
cookies or tokens sent along with every request.</p>

<h3 id="anatomy-of-a-request">Anatomy of a request</h3>

<figure class="highlight"><pre><code class="language-http" data-lang="http"><span class="nf">GET</span> <span class="nn">/articles/42</span> <span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span>
<span class="na">Host</span><span class="p">:</span> <span class="s">example.com</span>
<span class="na">User-Agent</span><span class="p">:</span> <span class="s">Mozilla/5.0</span>
<span class="na">Accept</span><span class="p">:</span> <span class="s">text/html</span>
<span class="na">Cookie</span><span class="p">:</span> <span class="s">session=abc123</span></code></pre></figure>

<p>Four parts: a <strong>method</strong> (<code class="language-plaintext highlighter-rouge">GET</code>), a <strong>path</strong> (<code class="language-plaintext highlighter-rouge">/articles/42</code>), <strong>headers</strong> (<code class="language-plaintext highlighter-rouge">Name: value</code> metadata lines), and
an optional <strong>body</strong>: data you’re sending, e.g. form contents or <abbr title="JavaScript Object Notation">JSON</abbr>. <code class="language-plaintext highlighter-rouge">GET</code> requests carry no body;
<code class="language-plaintext highlighter-rouge">POST</code>/<code class="language-plaintext highlighter-rouge">PUT</code> usually do.</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Method</th><th scope="col">Meaning</th><th scope="col">Safe?</th><th scope="col">Idempotent?</th></tr>
    </thead>
    <tbody>
      <tr><td><code>GET</code></td><td>Read a resource</td><td>Yes</td><td>Yes</td></tr>
      <tr><td><code>POST</code></td><td>Create / submit data</td><td>No</td><td>No</td></tr>
      <tr><td><code>PUT</code></td><td>Replace a resource</td><td>No</td><td>Yes</td></tr>
      <tr><td><code>PATCH</code></td><td>Partially update</td><td>No</td><td>Not guaranteed</td></tr>
      <tr><td><code>DELETE</code></td><td>Remove a resource</td><td>No</td><td>Yes</td></tr>
    </tbody>
  </table>
</div>

<p><em>Safe</em> = doesn’t change anything on the server. <em>Idempotent</em> = doing it twice has the same effect as doing it
once, which is why retrying a <code class="language-plaintext highlighter-rouge">POST</code> risks a duplicate order, while retrying a <code class="language-plaintext highlighter-rouge">PUT</code> doesn’t.</p>

<p>Keep in mind these meanings are conventions, not enforcement: the server runs whatever code it wants for any
method. The conventions still matter, because everything around you (caches, browsers, proxies, retry logic)
assumes you follow them.</p>

<h3 id="paths-and-query-parameters">Paths and query parameters</h3>

<p>The <strong>path</strong> is how the server decides <em>which</em> resource or piece of code handles the request. On a static
site, <code class="language-plaintext highlighter-rouge">/about</code> might map straight to an <code class="language-plaintext highlighter-rouge">about.html</code> file. In an <abbr title="Application Programming Interface">API</abbr>, paths typically name data and its
hierarchy:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">/users            → the collection of users
/users/42         → the user with id 42
/users/42/posts   → that user's posts</code></pre></figure>

<p><strong>Query parameters</strong>, the <code class="language-plaintext highlighter-rouge">?key=value&amp;key2=value2</code> tail of a <abbr title="Uniform Resource Locator">URL</abbr>, refine the request without changing which
resource it targets:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">/products?category=books&amp;sort=price&amp;page=2</code></pre></figure>

<p>Same resource (<code class="language-plaintext highlighter-rouge">/products</code>), but filtered, sorted, and paginated. Filtering, searching, sorting, and
pagination are the classic jobs of query parameters. The server reads them as ordinary key-value inputs;
nothing about them is magic.</p>

<h3 id="anatomy-of-a-response">Anatomy of a response</h3>

<figure class="highlight"><pre><code class="language-http" data-lang="http"><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
<span class="na">Content-Type</span><span class="p">:</span> <span class="s">text/html; charset=utf-8</span>
<span class="na">Content-Length</span><span class="p">:</span> <span class="s">5320</span>
<span class="na">Cache-Control</span><span class="p">:</span> <span class="s">max-age=3600</span>
<span class="na">Set-Cookie</span><span class="p">:</span> <span class="s">session=abc123; HttpOnly</span>

<span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>...</code></pre></figure>

<p>A <strong>status line</strong>, then <strong>headers</strong>, then the <strong>body</strong>, which carries the actual content.</p>

<p>The first digit of the status code tells you the category. Memorize the shape, not the full list:
<strong>2 good, 3 look elsewhere, 4 your fault, 5 my fault.</strong></p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Range</th><th scope="col">Meaning</th><th scope="col">Common examples</th></tr>
    </thead>
    <tbody>
      <tr><td>1xx</td><td>Informational</td><td><code>101 Switching Protocols</code></td></tr>
      <tr><td>2xx</td><td>Success</td><td><code>200 OK</code>, <code>201 Created</code>, <code>204 No Content</code></td></tr>
      <tr><td>3xx</td><td>Redirection</td><td><code>301 Moved Permanently</code>, <code>302 Found</code>, <code>304 Not Modified</code></td></tr>
      <tr><td>4xx</td><td>Client made a mistake</td><td><code>400 Bad Request</code>, <code>401 Unauthorized</code>, <code>403 Forbidden</code>, <code>404 Not Found</code>, <code>429 Too Many Requests</code></td></tr>
      <tr><td>5xx</td><td>Server failed</td><td><code>500 Internal Server Error</code>, <code>502 Bad Gateway</code>, <code>503 Service Unavailable</code></td></tr>
    </tbody>
  </table>
</div>

<details class="article-exercise">
  <summary>📋 Headers worth knowing</summary>

  <div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Header</th><th scope="col">Direction</th><th scope="col">Purpose</th></tr>
    </thead>
    <tbody>
      <tr><td><code>Host</code></td><td>request</td><td>Which site on this server you want (one IP can host many sites)</td></tr>
      <tr><td><code>Content-Type</code></td><td>both</td><td>Format of the body (<code>text/html</code>, <code>application/json</code>…)</td></tr>
      <tr><td><code>Authorization</code></td><td>request</td><td>Credentials, e.g. <code>Bearer &lt;token&gt;</code></td></tr>
      <tr><td><code>Cookie</code> / <code>Set-Cookie</code></td><td>req / resp</td><td>Client-side state, sessions</td></tr>
      <tr><td><code>Cache-Control</code></td><td>both</td><td>Whether/how long the response may be cached</td></tr>
      <tr><td><code>Location</code></td><td>response</td><td>Where a redirect points</td></tr>
      <tr><td><code>Accept</code></td><td>request</td><td>Formats the client can handle</td></tr>
    </tbody>
  </table>
</div>

</details>

<h3 id="not-just-pages-http-is-the-api-protocol">Not just pages: <abbr title="HyperText Transfer Protocol">HTTP</abbr> is the <abbr title="Application Programming Interface">API</abbr> protocol</h3>

<p>Everything above applies far beyond loading web pages. When one program talks to another over the web (a
mobile app syncing messages, JavaScript refreshing a feed, one backend calling another), it’s almost always
the same <abbr title="HyperText Transfer Protocol">HTTP</abbr> machinery carrying <strong><abbr title="JavaScript Object Notation">JSON</abbr></strong> instead of <abbr title="HyperText Markup Language">HTML</abbr>:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">GET /api/me            →    200 OK
                            Content-Type: application/json

                            { "id": 7, "name": "Sam", "role": "admin" }</code></pre></figure>

<p>Nothing new is happening: same methods, same status codes, same headers; only the body format and the
consumer change. The response isn’t shown to a human; code reads it and updates the page or acts on it. This
is why learning <abbr title="HyperText Transfer Protocol">HTTP</abbr> once pays off twice: it’s both how websites load and how virtually every web <abbr title="Application Programming Interface">API</abbr> works.</p>

<h3 id="https-http--encryption"><abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr>: <abbr title="HyperText Transfer Protocol">HTTP</abbr> + encryption</h3>

<p>Plain <abbr title="HyperText Transfer Protocol">HTTP</abbr> is readable by anyone on the path: your Wi-Fi neighbors, your <abbr title="Internet Service Provider">ISP</abbr>, anyone in between. <strong><abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr></strong>
wraps <abbr title="HyperText Transfer Protocol">HTTP</abbr> inside <strong><abbr title="Transport Layer Security">TLS</abbr></strong> (Transport Layer Security), which adds three guarantees:</p>

<ul>
  <li><strong>Encryption</strong>: nobody in the middle can read the traffic.</li>
  <li><strong>Integrity</strong>: nobody can modify it undetected.</li>
  <li><strong>Authentication</strong>: a <em>certificate</em>, signed by a trusted Certificate Authority, proves you’re talking to
the real <code class="language-plaintext highlighter-rouge">example.com</code>, not an impostor.</li>
</ul>

<p>The padlock in your browser means the <abbr title="Transport Layer Security">TLS</abbr> handshake succeeded and the certificate checks out. It does <strong>not</strong>
mean the site itself is trustworthy; phishing sites use <abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr> too. Certificates are free today
(<a href="https://letsencrypt.org/">Let’s Encrypt</a>), so there is essentially no excuse for a site to be <abbr title="HyperText Transfer Protocol">HTTP</abbr>-only.</p>

<h3 id="http-versions-in-one-paragraph"><abbr title="HyperText Transfer Protocol">HTTP</abbr> versions in one paragraph</h3>

<p><strong><abbr title="HyperText Transfer Protocol">HTTP</abbr>/1.1</strong> (1997) allows one request at a time per connection, so browsers open several connections in
parallel to compensate. <strong><abbr title="HyperText Transfer Protocol">HTTP</abbr>/2</strong> (2015) multiplexes many requests over one connection and adds binary
framing and header compression. <strong><abbr title="HyperText Transfer Protocol">HTTP</abbr>/3</strong> (2022) replaces <abbr title="Transmission Control Protocol">TCP</abbr> with <strong><abbr title="a transport protocol built on UDP; originally &quot;Quick UDP Internet Connections&quot;">QUIC</abbr></strong>. A common confusion: <abbr title="a transport protocol built on UDP; originally &quot;Quick UDP Internet Connections&quot;">QUIC</abbr> is not
“just <abbr title="User Datagram Protocol">UDP</abbr>”. <abbr title="User Datagram Protocol">UDP</abbr> alone gives you none of <abbr title="Transmission Control Protocol">TCP</abbr>’s guarantees: no ordering, no retransmission, no concept of a
connection. <abbr title="a transport protocol built on UDP; originally &quot;Quick UDP Internet Connections&quot;">QUIC</abbr> is a full transport protocol that <em>rebuilds</em> those guarantees on top of <abbr title="User Datagram Protocol">UDP</abbr>, and adds two
things <abbr title="Transmission Control Protocol">TCP</abbr> can’t offer: <abbr title="Transport Layer Security">TLS</abbr> encryption built into the handshake (fewer round trips before data flows), and
independent delivery per request, so one lost packet stalls only the request it belongs to instead of
everything behind it, as happens with <abbr title="Transmission Control Protocol">TCP</abbr>. As a developer you rarely change your code between versions; the
semantics (methods, statuses, headers) stay the same.</p>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># See a raw response, headers included</span>
curl <span class="nt">-i</span> https://example.com

<span class="c"># Headers only</span>
curl <span class="nt">-I</span> https://example.com

<span class="c"># Follow redirects and watch them happen</span>
curl <span class="nt">-iL</span> http://github.com

<span class="c"># Send JSON</span>
curl <span class="nt">-X</span> POST https://httpbin.org/post <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"hello": "world"}'</span></code></pre></figure>

  <p>Then open DevTools → <strong>Network</strong>, reload any page, and click a request. Everything in this section is right
there: method, status, headers, body.</p>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>Why does a retry of a <code>POST</code> risk creating a duplicate order, while retrying a <code>PUT</code> doesn't?</summary>

    <p><code class="language-plaintext highlighter-rouge">POST</code> is not idempotent: each attempt creates a new resource, so two attempts can mean two orders. <code class="language-plaintext highlighter-rouge">PUT</code>
replaces the resource with the same content, so repeating it leaves the server in the same state.</p>

  </details>

  <details class="article-answer">
    <summary>What's the difference between <code>401</code> and <code>403</code>?</summary>

    <p><code class="language-plaintext highlighter-rouge">401 Unauthorized</code> means “you haven’t proven who you are”: credentials are missing or invalid, so
authenticate and try again. <code class="language-plaintext highlighter-rouge">403 Forbidden</code> means “I know who you are, and you’re still not allowed.”</p>

  </details>

  <details class="article-answer">
    <summary>What three guarantees does <abbr title="Transport Layer Security">TLS</abbr> add on top of <abbr title="HyperText Transfer Protocol">HTTP</abbr>?</summary>

    <p>Encryption (nobody in the middle can read the traffic), integrity (nobody can modify it undetected), and
authentication (a certificate proves you’re talking to the real site).</p>

  </details>

  <details class="article-answer">
    <summary>The server sends <code>Cache-Control: max-age=3600</code>. What happens if you reload the page after ten minutes?</summary>

    <p>Nothing goes over the network. <code class="language-plaintext highlighter-rouge">max-age=3600</code> allows caching for one hour, so ten minutes in, the browser
serves its cached copy from disk without contacting the server at all.</p>

  </details>

</details>

<h2 id="domain-names" class="article-section">Domain names: leasing your corner of the namespace</h2>

<p>Machines find each other by <abbr title="Internet Protocol">IP</abbr> address, but <code class="language-plaintext highlighter-rouge">142.250.185.78</code> is hostile to humans, and a site’s <abbr title="Internet Protocol">IP</abbr> can change
when it moves servers. <strong>Domain names</strong> are stable, memorable labels (<code class="language-plaintext highlighter-rouge">example.com</code>) that get translated to <abbr title="Internet Protocol">IP</abbr>
addresses on demand (that translation is <a href="#dns"><abbr title="Domain Name System">DNS</abbr></a>). This section is about the <em>names themselves</em>: who owns
them, who manages them, and how the pieces fit together.</p>

<h3 id="anatomy-of-a-domain">Anatomy of a domain</h3>

<p>Read a domain <strong>right to left</strong>: it’s a hierarchy.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text"> blog.example.com.
  │      │     │ └── root (implicit trailing dot)
  │      │     └──── top-level domain (TLD)
  │      └────────── second-level domain (the part you register)
  └───────────────── subdomain (you create these freely)</code></pre></figure>

<ul>
  <li><strong>Root</strong>: the invisible top of the hierarchy, managed globally.</li>
  <li><strong><abbr title="top-level domain">TLD</abbr></strong>: <code class="language-plaintext highlighter-rouge">.com</code>, <code class="language-plaintext highlighter-rouge">.org</code>, <code class="language-plaintext highlighter-rouge">.dev</code>, plus country codes like <code class="language-plaintext highlighter-rouge">.cz</code>, <code class="language-plaintext highlighter-rouge">.de</code>, <code class="language-plaintext highlighter-rouge">.uk</code>. Each <abbr title="top-level domain">TLD</abbr> is operated by a
<strong>registry</strong>.</li>
  <li><strong>Second-level domain</strong>: <code class="language-plaintext highlighter-rouge">example</code> in <code class="language-plaintext highlighter-rouge">example.com</code>. This is the unit you actually register and pay for.</li>
  <li><strong>Subdomains</strong>: <code class="language-plaintext highlighter-rouge">blog.</code>, <code class="language-plaintext highlighter-rouge">shop.</code>, <code class="language-plaintext highlighter-rouge">api.</code>. Once you own <code class="language-plaintext highlighter-rouge">example.com</code>, you can create unlimited subdomains
at no cost, just by adding <abbr title="Domain Name System">DNS</abbr> records.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">www</code> is nothing special, just a conventional subdomain. <code class="language-plaintext highlighter-rouge">www.example.com</code> and <code class="language-plaintext highlighter-rouge">example.com</code> are technically
two different names, which is why sites configure one to redirect to the other.</p>

<p>A <strong><abbr title="Uniform Resource Locator">URL</abbr></strong> contains more than the domain:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">https://blog.example.com:443/posts/hello?ref=home#comments
└─┬──┘  └───────┬───────┘└┬┘└────┬─────┘└───┬───┘└───┬───┘
scheme       domain      port   path      query   fragment</code></pre></figure>

<h3 id="the-players">The players</h3>

<p>Three roles, often confused:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Role</th><th scope="col">Who they are</th><th scope="col">Example</th></tr>
    </thead>
    <tbody>
      <tr><td><strong>Registry</strong></td><td>Operates an entire TLD, keeps its master database</td><td>Verisign runs <code>.com</code>; CZ.NIC runs <code>.cz</code></td></tr>
      <tr><td><strong>Registrar</strong></td><td>Retail company that sells registrations to the public</td><td>Namecheap, Cloudflare, Porkbun</td></tr>
      <tr><td><strong>Registrant</strong></td><td>You: the person or company holding the domain</td><td>—</td></tr>
    </tbody>
  </table>
</div>

<p>Above the registries sits <strong><abbr title="Internet Corporation for Assigned Names and Numbers">ICANN</abbr></strong>, the nonprofit that coordinates the global naming system and accredits
registrars.</p>

<p>Important mental model: <strong>you never buy a domain; you lease it.</strong> Registration is for 1–10 years and must be
renewed. Miss the renewal and the domain eventually returns to the open market (after a grace/redemption
period), where domain squatters are happy to grab anything with traffic.</p>

<h3 id="what-registration-actually-gets-you">What registration actually gets you</h3>

<p>When you register <code class="language-plaintext highlighter-rouge">example.com</code> at a registrar:</p>

<ol>
  <li>The registrar records you as registrant in the <code class="language-plaintext highlighter-rouge">.com</code> registry database.</li>
  <li>You provide contact details (<strong>WHOIS</strong> data, usually hidden behind free privacy protection nowadays).</li>
  <li>You tell the registry which <strong>nameservers</strong> answer questions about your domain. This is the hook that
connects your domain to <a href="#dns"><abbr title="Domain Name System">DNS</abbr></a>. By default these are your registrar’s nameservers, but you can point
them anywhere (e.g. Cloudflare’s).</li>
</ol>

<p>That’s all a domain is: an entry in a registry database plus a delegation saying “ask <em>these</em> nameservers
about this name.”</p>

<details class="article-exercise">
  <summary>💡 Practical notes on choosing and managing domains</summary>

  <ul>
    <li><strong>Pricing games:</strong> first-year prices are often loss-leaders (<code class="language-plaintext highlighter-rouge">$0.99</code>!) with steep renewal prices. Always
check the <em>renewal</em> cost.</li>
    <li><strong><abbr title="top-level domain">TLD</abbr> choice:</strong> <code class="language-plaintext highlighter-rouge">.com</code> is still what people type on autopilot. Country <abbr title="top-level domains">TLDs</abbr> are fine for local audiences.
Some <abbr title="top-level domains">TLDs</abbr> (<code class="language-plaintext highlighter-rouge">.dev</code>, <code class="language-plaintext highlighter-rouge">.app</code>) force <abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr> at the browser level, a nice property.</li>
    <li><strong>Transfers:</strong> you can move a domain between registrars. It requires unlocking the domain and an
authorization code, a mild hassle by design to prevent theft.</li>
    <li><strong>Domain ≠ hosting:</strong> registering a domain gives you a name, not a place to put a website, even if
registrars love to bundle them.</li>
    <li><strong>Security:</strong> protect the registrar account with <abbr title="two-factor authentication">2FA</abbr>. Whoever controls the account controls the
nameservers, and therefore your email, website, everything.</li>
  </ul>

</details>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <ol>
    <li>Look up any domain’s public record: <code class="language-plaintext highlighter-rouge">whois example.com</code> in a terminal (or a web WHOIS tool). Find the
registrar, creation date, expiry date, and nameservers.</li>
    <li>Price a domain you like at two different registrars; compare first-year vs. renewal pricing.</li>
    <li>Find the nameservers of a big site: <code class="language-plaintext highlighter-rouge">dig NS github.com +short</code>.</li>
  </ol>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>In <code>docs.api.example.org</code>, which part did someone pay for, and which parts are free?</summary>

    <p>Someone paid to register <code class="language-plaintext highlighter-rouge">example.org</code> (the second-level domain under the <code class="language-plaintext highlighter-rouge">.org</code> <abbr title="top-level domain">TLD</abbr>). The <code class="language-plaintext highlighter-rouge">api</code> and <code class="language-plaintext highlighter-rouge">docs</code>
subdomains are free: the owner created them just by adding <abbr title="Domain Name System">DNS</abbr> records.</p>

  </details>

  <details class="article-answer">
    <summary>What's the difference between a registry and a registrar?</summary>

    <p>A registry operates an entire <abbr title="top-level domain">TLD</abbr> and keeps its master database (Verisign for <code class="language-plaintext highlighter-rouge">.com</code>). A registrar is the
retail company that sells registrations to the public (Namecheap, Cloudflare, Porkbun) and records them in
the registry on your behalf.</p>

  </details>

  <details class="article-answer">
    <summary>Why is "buying a domain" technically the wrong phrase?</summary>

    <p>Because registration is a lease for 1–10 years, renewable. Stop paying and the domain eventually returns to
the open market. You never permanently own it.</p>

  </details>

  <details class="article-answer">
    <summary>You registered a domain but haven't set up hosting. What does a visitor see, and why?</summary>

    <p>An error such as “server not found”, or a registrar parking page if the registrar set one up. A domain is
only an entry in a registry database; until <abbr title="Domain Name System">DNS</abbr> records point the name at a server that answers, there’s
nothing to show.</p>

  </details>

</details>

<h2 id="hosting" class="article-section">Hosting: where websites actually live</h2>

<p><strong>Hosting</strong> means keeping your site’s files and code on a computer that is switched on, connected to the
internet, and reachable 24/7, so that when someone’s browser asks for your site, something answers.</p>

<p>You <em>could</em> host a site from your laptop. In practice you don’t, because your laptop sleeps, your home <abbr title="Internet Protocol">IP</abbr>
changes, your upload bandwidth is small, and one viral moment would melt it. So you rent capacity from
companies whose entire job is keeping servers online.</p>

<h3 id="static-vs-dynamic-the-distinction-that-matters">Static vs. dynamic: the distinction that matters</h3>

<p><strong>Static hosting:</strong> your site is just <strong>files</strong> (<abbr title="HyperText Markup Language">HTML</abbr>, <abbr title="Cascading Style Sheets">CSS</abbr>, JavaScript, images) prepared in advance. The
server’s only job is to hand them out, unchanged, to whoever asks. Every visitor gets the same files. It’s
extremely cheap (often free), extremely fast, and there’s almost nothing to break or hack. Perfect for
portfolios, blogs, documentation, and any app whose logic runs entirely in the browser. Typical providers:
GitHub Pages, Netlify, Vercel, Cloudflare Pages.</p>

<p>Note: a static <em>site</em> can still feel dynamic, because JavaScript in the browser can fetch data from <abbr title="Application Programming Interfaces">APIs</abbr>. “Static”
describes the hosting, not the user experience.</p>

<p><strong>Dynamic hosting:</strong> your site is a <strong>program</strong> that runs on the server and builds responses on the fly:
reading databases, checking who’s logged in, processing payments. Different visitors get different responses.
It needs a runtime (Node.js, Python, PHP, Go…) executing server-side. This is what anything with accounts,
user-generated content, or a database requires.</p>

<h3 id="flavors-of-dynamic-hosting">Flavors of dynamic hosting</h3>

<p>Every option here runs your code on somebody else’s computers. What actually differs between them is <strong>how
much of the stack you manage yourself and how much the provider takes off your hands</strong>. Read them as a
ladder, from least control (and least responsibility) to most:</p>

<ul>
  <li><strong>Shared hosting.</strong> You rent a folder on a server that hundreds of other customers share. You upload files
(classically PHP) and the host manages everything else: hardware, operating system, web server, updates.
Cheap and simple, but you can’t install software or tune anything. The classic cPanel/WordPress world.</li>
  <li><strong><abbr title="platform as a service">PaaS</abbr> (platform as a service).</strong> You hand the platform your application code and it does the rest: builds
it, runs it, restarts it when it crashes, scales it when traffic grows. You never see an operating system.
Heroku, Render, Railway, and Fly.io live here.</li>
  <li><strong>Serverless (functions).</strong> You don’t deploy a continuously running program at all. You upload individual
functions, and the platform spins one up when a request arrives and tears it down afterwards. You pay per
execution and scaling is automatic, but anything long-lived has to be stored elsewhere (a database, a
queue). AWS Lambda and Cloudflare Workers are the canonical examples.</li>
  <li><strong><abbr title="virtual private server">VPS</abbr> (virtual private server).</strong> You rent a virtual machine: a bare Linux box with root access. You get
full freedom to install and configure anything, and full responsibility for every security patch, backup,
and 3 a.m. outage. DigitalOcean, Hetzner, and Linode are typical providers.</li>
  <li><strong>Cloud (<abbr title="infrastructure as a service">IaaS</abbr>, infrastructure as a service).</strong> The <abbr title="virtual private server">VPS</abbr> idea at industrial scale: raw building blocks
(virtual machines, networks, storage, managed databases) that you compose however you like, at any scale.
Maximum power, maximum complexity. AWS, Google Cloud, and Azure.</li>
</ul>

<p>The trade-off runs one direction the whole way down that ladder: more control means more responsibility. A
<abbr title="platform as a service">PaaS</abbr> protects you from server administration but decides how your app must be built and what it costs to
scale; a <abbr title="virtual private server">VPS</abbr> asks nothing and provides nothing.</p>

<h3 id="cdns-hostings-speed-layer"><abbr title="Content Delivery Networks">CDNs</abbr>: hosting’s speed layer</h3>

<p>A <strong><abbr title="Content Delivery Network">CDN</abbr> (Content Delivery Network)</strong> is a worldwide fleet of servers that keep <strong>cached copies</strong> of your
content close to users. A visitor in Tokyo gets your files from a Tokyo edge server instead of crossing the
planet to your origin machine.</p>

<p>What <abbr title="Content Delivery Networks">CDNs</abbr> buy you: <strong>speed</strong> (physics is real; shorter distance = lower latency), <strong>resilience</strong> (traffic
spikes hit the edge cache, not your origin), and <strong>protection</strong> (most <abbr title="Content Delivery Networks">CDNs</abbr> absorb <abbr title="Distributed Denial of Service">DDoS</abbr> attacks and terminate
<abbr title="Transport Layer Security">TLS</abbr> for you).</p>

<p>Static assets are ideal <abbr title="Content Delivery Network">CDN</abbr> material. Modern static hosts (Netlify, Vercel, Cloudflare Pages) <em>are</em>
effectively <abbr title="Content Delivery Networks">CDNs</abbr>: your whole site lives at the edge. Common standalone <abbr title="Content Delivery Networks">CDNs</abbr>: Cloudflare, Fastly, CloudFront.</p>

<h3 id="deployment-getting-code-onto-hosting">Deployment: getting code onto hosting</h3>

<p><strong>Deployment</strong> is the process of moving your site from your machine to the host:</p>

<ol>
  <li><strong>The old way:</strong> drag files onto the server over <abbr title="File Transfer Protocol">FTP</abbr>. Error-prone, no history, no undo.</li>
  <li><strong>The current way, git-based:</strong> push to a Git repository; the platform detects the push, builds the site,
and publishes it automatically (<strong><abbr title="continuous integration / continuous delivery">CI/CD</abbr></strong>).</li>
</ol>

<figure class="highlight"><pre><code class="language-text" data-lang="text">git push → platform builds → tests run → deploy to edge → live in ~1 minute</code></pre></figure>

<p>Good platforms add <strong>preview deployments</strong> (every branch gets its own <abbr title="Uniform Resource Locator">URL</abbr>), <strong>instant rollback</strong> (bad release?
one click back), and <strong>environment variables</strong> (secrets like <abbr title="Application Programming Interface">API</abbr> keys configured on the platform, never
committed to the repo).</p>

<h3 id="custom-domains-connecting-the-name-to-the-host">Custom domains: connecting the name to the host</h3>

<p>Hosting providers give you a working default address like <code class="language-plaintext highlighter-rouge">yourproject.netlify.app</code>. To serve the site at
<strong>your own domain</strong>:</p>

<ol>
  <li>Register the domain (<a href="#domain-names">previous section</a>).</li>
  <li>In the hosting dashboard, add the custom domain, so the host expects traffic for that name.</li>
  <li>In your <abbr title="Domain Name System">DNS</abbr> settings, point the domain at the host: an <code class="language-plaintext highlighter-rouge">A</code> record to an <abbr title="Internet Protocol">IP</abbr>, or a <code class="language-plaintext highlighter-rouge">CNAME</code> to the host’s
address (exact records in the <a href="#dns">next section</a>).</li>
  <li>The host provisions a free <abbr title="Transport Layer Security">TLS</abbr> certificate automatically, so <abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr> just works.</li>
</ol>

<p>The domain, the <abbr title="Domain Name System">DNS</abbr>, and the hosting can live at three different companies; they cooperate through exactly
this mechanism.</p>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <ol>
    <li>Create a file <code class="language-plaintext highlighter-rouge">index.html</code> with anything in it, push it to a GitHub repository, and enable
<strong>GitHub Pages</strong> in the repo settings. You now have a hosted website, at a total cost of zero.</li>
    <li>Open DevTools → Network on a big site and look at response headers like <code class="language-plaintext highlighter-rouge">cf-cache-status</code>, <code class="language-plaintext highlighter-rouge">x-cache</code>,
<code class="language-plaintext highlighter-rouge">server</code>, or <code class="language-plaintext highlighter-rouge">age</code>, evidence of a <abbr title="Content Delivery Network">CDN</abbr> answering instead of the origin.</li>
  </ol>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>Your blog is pure <abbr title="HyperText Markup Language">HTML</abbr>/<abbr title="Cascading Style Sheets">CSS</abbr>. Which hosting model fits, and roughly what should it cost?</summary>

    <p>Static hosting. It should cost nothing, or close to it: GitHub Pages, Cloudflare Pages, and Netlify all
serve static sites for free.</p>

  </details>

  <details class="article-answer">
    <summary>Why can't a login system run on purely static hosting?</summary>

    <p>Logging in requires code running on the server: verifying credentials against a database, creating a
session, deciding per-visitor what to show. A static host only hands out prepared files, identical for
everyone.</p>

  </details>

  <details class="article-answer">
    <summary>What problem does a <abbr title="Content Delivery Network">CDN</abbr> solve that faster servers cannot?</summary>

    <p>Distance. Latency comes from how far packets have to travel, and no amount of server horsepower moves Tokyo
closer to Frankfurt. A <abbr title="Content Delivery Network">CDN</abbr> serves the content from an edge node near the visitor instead. It also absorbs
traffic spikes and attacks that would otherwise hit your origin.</p>

  </details>

  <details class="article-answer">
    <summary>What are the moving parts between <code>git push</code> and your change being live?</summary>

    <p>The platform detects the push, builds the site from the source, runs any tests, publishes the result to its
servers or edge network, and switches the live version over. That chain is what <abbr title="continuous integration / continuous delivery">CI/CD</abbr> refers to.</p>

  </details>

</details>

<h2 id="dns" class="article-section"><abbr title="Domain Name System">DNS</abbr>: the lookup chain behind every click</h2>

<p><strong><abbr title="Domain Name System">DNS</abbr> (Domain Name System)</strong> is the internet’s phone book: a global, distributed database that translates
names (<code class="language-plaintext highlighter-rouge">example.com</code>) into <abbr title="Internet Protocol">IP</abbr> addresses (<code class="language-plaintext highlighter-rouge">93.184.216.34</code>) and other information. Every website visit, email
delivery, and <abbr title="Application Programming Interface">API</abbr> call starts with a <abbr title="Domain Name System">DNS</abbr> lookup.</p>

<p>No single server holds all the answers. <abbr title="Domain Name System">DNS</abbr> is a <strong>hierarchy of delegation</strong>: the root knows who runs each
<abbr title="top-level domain">TLD</abbr>, the <abbr title="top-level domain">TLD</abbr> knows who runs each domain, and the domain’s own nameservers hold the actual records.</p>

<h3 id="a-lookup-step-by-step">A lookup, step by step</h3>

<p>You type <code class="language-plaintext highlighter-rouge">blog.example.com</code>. Here’s the full journey (the “cold” path; caching usually shortcuts most of it):</p>

<ol>
  <li><strong>Browser &amp; <abbr title="operating system">OS</abbr> cache</strong>: have we looked this up recently? If yes, done.</li>
  <li><strong>Resolver</strong>: your machine asks a <strong>recursive resolver</strong> (run by your <abbr title="Internet Service Provider">ISP</abbr>, or a public one like <code class="language-plaintext highlighter-rouge">1.1.1.1</code>
or <code class="language-plaintext highlighter-rouge">8.8.8.8</code>). The resolver does the legwork:</li>
  <li><strong>Root nameservers</strong> → “Who handles <code class="language-plaintext highlighter-rouge">.com</code>?” → referral to the <code class="language-plaintext highlighter-rouge">.com</code> <abbr title="top-level domain">TLD</abbr> servers.</li>
  <li><strong><abbr title="top-level domain">TLD</abbr> nameservers</strong> → “Who handles <code class="language-plaintext highlighter-rouge">example.com</code>?” → referral to that domain’s <strong>authoritative
nameservers</strong> (the ones set at the registrar).</li>
  <li><strong>Authoritative nameservers</strong> → “What’s the address of <code class="language-plaintext highlighter-rouge">blog.example.com</code>?” → the actual answer:
<code class="language-plaintext highlighter-rouge">93.184.216.34</code>.</li>
  <li>The resolver returns the answer and <strong>caches it</strong> for next time.</li>
</ol>

<p>Total cost: a few milliseconds when cached, maybe 20–120 ms cold.</p>

<p>Two roles worth keeping straight: the <strong>recursive resolver</strong> is the <em>asker</em> that chases referrals on your
behalf; an <strong>authoritative nameserver</strong> is the <em>answerer</em> that holds the real records for a domain.</p>

<h3 id="dns-records"><abbr title="Domain Name System">DNS</abbr> records</h3>

<p>A domain’s zone is a set of typed records. The ones you’ll actually touch:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Record</th><th scope="col">Maps</th><th scope="col">Example use</th></tr>
    </thead>
    <tbody>
      <tr><td><strong><abbr title="Address record">A</abbr></strong></td><td>name → IPv4 address</td><td><code>example.com → 93.184.216.34</code></td></tr>
      <tr><td><strong><abbr title="IPv6 address record">AAAA</abbr></strong></td><td>name → IPv6 address</td><td>same, for IPv6</td></tr>
      <tr><td><strong><abbr title="Canonical Name record">CNAME</abbr></strong></td><td>name → another name</td><td><code>www → example.com</code>, or <code>blog → myapp.netlify.app</code></td></tr>
      <tr><td><strong><abbr title="Mail Exchange record">MX</abbr></strong></td><td>domain → mail servers (with priority)</td><td>route email to Google Workspace / Fastmail</td></tr>
      <tr><td><strong><abbr title="Text record">TXT</abbr></strong></td><td>name → arbitrary text</td><td>ownership proofs; email security (<abbr title="Sender Policy Framework">SPF</abbr>, <abbr title="DomainKeys Identified Mail">DKIM</abbr>, <abbr title="Domain-based Message Authentication, Reporting and Conformance">DMARC</abbr>)</td></tr>
      <tr><td><strong><abbr title="Nameserver record">NS</abbr></strong></td><td>domain → its authoritative nameservers</td><td>the delegation itself</td></tr>
    </tbody>
  </table>
</div>

<p>Rules of thumb:</p>

<ul>
  <li><strong>A/AAAA</strong> when you have an <abbr title="Internet Protocol">IP</abbr> address; <strong>CNAME</strong> when you have a hostname.</li>
  <li><strong><abbr title="Mail Exchange">MX</abbr></strong> is why your website and your email can live at completely different companies under one domain.</li>
  <li><strong><abbr title="text record">TXT</abbr></strong> is the duct tape of <abbr title="Domain Name System">DNS</abbr>: every service that says “add this record to verify ownership” is using <abbr title="text record">TXT</abbr>.</li>
</ul>

<h3 id="cname-slowly">CNAME, slowly</h3>

<p>CNAME confuses almost everyone at first, so it deserves a slower walkthrough. A CNAME is an <strong>alias</strong>. It
says: “this name has no address of its own; look up that <em>other</em> name instead, and use whatever you find
there.” Two concrete examples:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">www.example.com.    CNAME    example.com.</code></pre></figure>

<p>Read it as: “to resolve <code class="language-plaintext highlighter-rouge">www.example.com</code>, go resolve <code class="language-plaintext highlighter-rouge">example.com</code> and use its answer.” The practical
effect: <code class="language-plaintext highlighter-rouge">www</code> and the bare domain always end up pointing to the same place, and when your server’s <abbr title="Internet Protocol">IP</abbr>
changes, you update one A record (the one on <code class="language-plaintext highlighter-rouge">example.com</code>) instead of two.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">blog.example.com.   CNAME    myapp.netlify.app.</code></pre></figure>

<p>Read it as: “to resolve <code class="language-plaintext highlighter-rouge">blog.example.com</code>, go resolve <code class="language-plaintext highlighter-rouge">myapp.netlify.app</code>, whatever that happens to resolve
to today.” This is why hosting providers ask you to add a CNAME rather than an A record: Netlify stays free
to change its servers’ <abbr title="Internet Protocol">IP</abbr> addresses at any time, and your blog follows automatically because every lookup
goes through their name. You never have to know or track their IPs.</p>

<p>One catch: because a CNAME means “this name has no data of its own, everything is over there,” it can’t
coexist with any other record on the same name. The bare domain always carries other records (at minimum
<abbr title="nameserver">NS</abbr>), which is why <code class="language-plaintext highlighter-rouge">example.com</code> itself traditionally needs an A record and can’t be a CNAME.</p>

<h3 id="caching-ttl-and-the-myth-of-propagation">Caching, <abbr title="time to live">TTL</abbr>, and the myth of “propagation”</h3>

<p>Every record carries a <strong><abbr title="time to live">TTL</abbr> (time to live)</strong> in seconds: how long any cache may keep the answer before
asking again.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">example.com.   3600   IN   A   93.184.216.34
                └── cache me for up to 1 hour</code></pre></figure>

<p>High <abbr title="time to live">TTL</abbr> (hours/days): fewer lookups, faster for users, but changes take longer to be seen everywhere. Low
<abbr title="time to live">TTL</abbr> (60–300 s): changes spread fast, at the cost of more lookup traffic. <strong>Pro move:</strong> before a planned
migration, lower the <abbr title="time to live">TTL</abbr> to 300 a day in advance; make the change; raise it back.</p>

<p>When people say a <abbr title="Domain Name System">DNS</abbr> change is “propagating,” nothing is actually being pushed anywhere. Your authoritative
server answers with the new value <strong>immediately</strong>. What you’re waiting for is the world’s caches to let their
old copies <strong>expire</strong> (per the old <abbr title="time to live">TTL</abbr>). Different resolvers cached at different moments, so some users see
the new value while others still see the old one. This is normal and resolves itself within the old <abbr title="time to live">TTL</abbr>.</p>

<h3 id="connecting-a-domain-to-hosting-in-records">Connecting a domain to hosting, in records</h3>

<p>The custom-domain workflow from the <a href="#hosting">hosting section</a>, expressed concretely:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Goal</th><th scope="col">Record you create</th></tr>
    </thead>
    <tbody>
      <tr><td>Bare domain → your host's IP</td><td><code>A  example.com → 76.76.21.21</code></td></tr>
      <tr><td><code>www</code> → same site</td><td><code>CNAME  www → example.com</code></td></tr>
      <tr><td>Subdomain → a platform</td><td><code>CNAME  blog → myapp.netlify.app</code></td></tr>
      <tr><td>Email at your domain</td><td><code>MX  example.com → mail provider</code></td></tr>
      <tr><td>Prove ownership to a service</td><td><code>TXT  example.com → "verification=…"</code></td></tr>
    </tbody>
  </table>
</div>

<p>And remember the top of the chain: the <strong><abbr title="nameserver">NS</abbr> records set at your registrar</strong> decide <em>whose</em> <abbr title="Domain Name System">DNS</abbr> answers at all.
Many people register at one company but point <abbr title="nameserver">NS</abbr> to Cloudflare and manage records there.</p>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Basic lookup</span>
dig example.com +short

<span class="c"># Full detail; find the TTL in the ANSWER section</span>
dig example.com

<span class="c"># Specific record types</span>
dig example.com MX +short
dig example.com TXT +short
dig example.com NS +short

<span class="c"># Watch the whole delegation chain (root → TLD → authoritative)</span>
dig example.com +trace

<span class="c"># Ask a specific resolver</span>
dig @1.1.1.1 example.com +short</code></pre></figure>

  <p>Run <code class="language-plaintext highlighter-rouge">dig</code> on the same domain twice and compare the <abbr title="times to live">TTLs</abbr>; the second answer comes from cache, counting down.</p>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>What's the difference between a recursive resolver and an authoritative nameserver?</summary>

    <p>The recursive resolver (your <abbr title="Internet Service Provider">ISP</abbr>’s, or <code class="language-plaintext highlighter-rouge">1.1.1.1</code>) chases the chain of referrals on your behalf and caches
the answers. The authoritative nameserver holds the domain’s actual records and gives the final answer.</p>

  </details>

  <details class="article-answer">
    <summary>Why can't the bare <code>example.com</code> be a <abbr title="Canonical Name record">CNAME</abbr> (in classic <abbr title="Domain Name System">DNS</abbr>)?</summary>

    <p>A CNAME can’t coexist with any other record on the same name, and the bare domain must carry other records,
at minimum its <abbr title="nameserver">NS</abbr> records. So the apex traditionally takes an A record instead.</p>

  </details>

  <details class="article-answer">
    <summary>You changed an <abbr title="Address record">A</abbr> record with <abbr title="time to live">TTL</abbr> 86400 and some users still see the old site five hours later. Is anything broken?</summary>

    <p>No. <abbr title="time to live">TTL</abbr> 86400 means caches may keep the old answer for up to 24 hours. Any resolver that cached the record
before your change keeps serving the old <abbr title="Internet Protocol">IP</abbr> until its copy expires, so users see the new site at different
times within that window.</p>

  </details>

  <details class="article-answer">
    <summary>Which record type controls where your email goes?</summary>

    <p><abbr title="Mail Exchange">MX</abbr>. It lists the mail servers for the domain, with priorities, independently of where the website points.</p>

  </details>

</details>

<h2 id="the-browser" class="article-section">The browser: from response bytes to rendered pixels</h2>

<p>A browser is a program that turns a <abbr title="Uniform Resource Locator">URL</abbr> into an interactive page. That involves four distinct jobs, in a
pipeline:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">fetch → parse &amp; render → execute JavaScript → store data / stay interactive</code></pre></figure>

<p>Under the hood, each browser is built on an <strong>engine</strong>: Chrome and Edge use Blink (with the V8 JavaScript
engine), Firefox uses Gecko (SpiderMonkey), Safari uses WebKit (JavaScriptCore). Engines differ slightly,
which is why “works in Chrome” isn’t a guarantee.</p>

<h3 id="fetching">Fetching</h3>

<p>You press Enter. The browser resolves the domain via <a href="#dns"><abbr title="Domain Name System">DNS</abbr></a>, opens a <abbr title="Transmission Control Protocol">TCP</abbr> + <abbr title="Transport Layer Security">TLS</abbr> connection, sends an
<a href="#speaking-http"><abbr title="HyperText Transfer Protocol">HTTP</abbr> request</a>, and receives an <abbr title="HyperText Markup Language">HTML</abbr> response.</p>

<p>But one <abbr title="HyperText Markup Language">HTML</abbr> file is never the whole page. As the browser reads the <abbr title="HyperText Markup Language">HTML</abbr>, it discovers <em>more</em> things to
fetch (stylesheets, scripts, images, fonts, <abbr title="Application Programming Interface">API</abbr> calls) and fires off dozens of additional requests, largely
in parallel. A typical page today triggers 50–100 requests. The browser also decides what it can skip:
resources cached from a previous visit (per <code class="language-plaintext highlighter-rouge">Cache-Control</code>) are reused straight from disk.</p>

<h3 id="rendering-the-critical-path">Rendering: the critical path</h3>

<ol>
  <li><strong>Parse <abbr title="HyperText Markup Language">HTML</abbr> → <abbr title="Document Object Model">DOM</abbr>.</strong> The <abbr title="HyperText Markup Language">HTML</abbr> text becomes the <strong><abbr title="Document Object Model">DOM</abbr></strong> (Document Object Model), a live tree of elements.
Broken <abbr title="HyperText Markup Language">HTML</abbr> doesn’t stop it; browsers repair as they go.</li>
  <li><strong>Parse <abbr title="Cascading Style Sheets">CSS</abbr> → <abbr title="CSS Object Model">CSSOM</abbr>.</strong> All stylesheets become a second structure describing the rules.</li>
  <li><strong><abbr title="Document Object Model">DOM</abbr> + <abbr title="CSS Object Model">CSSOM</abbr> → render tree.</strong> Only visible elements, each with its final computed style.</li>
  <li><strong>Layout (reflow).</strong> Compute exact geometry: where every box sits and how big it is.</li>
  <li><strong>Paint.</strong> Fill in pixels: text, colors, borders, images.</li>
  <li><strong>Composite.</strong> Layers (e.g. things with animations or <code class="language-plaintext highlighter-rouge">position: fixed</code>) are assembled on the <abbr title="Graphics Processing Unit">GPU</abbr> into the
final frame.</li>
</ol>

<p>Two practical consequences:</p>

<ul>
  <li><strong><abbr title="Cascading Style Sheets">CSS</abbr> blocks rendering</strong>: the browser won’t paint until it knows the styles (otherwise you’d see ugly
unstyled flashes).</li>
  <li><strong>Classic <code class="language-plaintext highlighter-rouge">&lt;script&gt;</code> tags block parsing</strong>: the parser stops, runs the script, then continues. That’s why
scripts go at the end of <code class="language-plaintext highlighter-rouge">&lt;body&gt;</code> or carry <code class="language-plaintext highlighter-rouge">defer</code>/<code class="language-plaintext highlighter-rouge">async</code> attributes.</li>
</ul>

<p>When JavaScript later changes the page, the browser redoes the necessary parts of this pipeline, which is why
heavy <abbr title="Document Object Model">DOM</abbr> manipulation can make pages feel sluggish.</p>

<h3 id="running-javascript">Running JavaScript</h3>

<p>JavaScript is what makes pages <em>do</em> things. The browser’s <abbr title="JavaScript">JS</abbr> engine executes code that can read and modify the
<abbr title="Document Object Model">DOM</abbr>, react to <strong>events</strong> (clicks, typing, scrolling), fetch more data without reloading (<code class="language-plaintext highlighter-rouge">fetch()</code>, the basis
of every single-page app), and, with permission, draw, animate, play audio, or access camera and location.</p>

<p>One crucial constraint: JavaScript on a page runs on a <strong>single main thread</strong>, shared with rendering.
Long-running <abbr title="JavaScript">JS</abbr> = frozen page. And for safety, all of it runs in a <strong>sandbox</strong>: page code cannot touch your
files or other tabs, and the <strong>same-origin policy</strong> stops a page from reading data belonging to another site.</p>

<h3 id="storing-site-data">Storing site data</h3>

<p>Browsers give sites several places to keep data on your machine:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Storage</th><th scope="col">Size</th><th scope="col">Lifetime</th><th scope="col">Typical use</th></tr>
    </thead>
    <tbody>
      <tr><td><strong>Cookies</strong></td><td>~4 KB each</td><td>set by expiry; sent to the server <strong>with every request</strong></td><td>sessions, "keep me logged in"</td></tr>
      <tr><td><strong>localStorage</strong></td><td>~5–10 MB</td><td>until cleared</td><td>preferences, e.g. dark mode</td></tr>
      <tr><td><strong>sessionStorage</strong></td><td>~5 MB</td><td>until the tab closes</td><td>temporary state</td></tr>
      <tr><td><strong>IndexedDB</strong></td><td>large</td><td>until cleared</td><td>offline apps, big structured data</td></tr>
      <tr><td><strong>Cache Storage</strong></td><td>large</td><td>managed by service workers</td><td>offline pages, <abbr title="Progressive Web Apps">PWAs</abbr></td></tr>
    </tbody>
  </table>
</div>

<p>The key difference: <strong>cookies travel to the server automatically; the rest stay in the browser</strong> unless code
sends them. That’s why login sessions use cookies, and also why cookies are the mechanism behind cross-site
tracking and why browsers increasingly restrict third-party cookies.</p>

<h3 id="devtools-x-ray-vision">DevTools: X-ray vision</h3>

<p>Every desktop browser ships professional-grade inspection tools; press <strong>F12</strong> or right-click → <em>Inspect</em>.
The tabs map exactly onto this article:</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Tab</th><th scope="col">What it shows</th></tr>
    </thead>
    <tbody>
      <tr><td><strong>Elements</strong></td><td>The live DOM + applied CSS; edit both in place</td></tr>
      <tr><td><strong>Console</strong></td><td><abbr title="JavaScript">JS</abbr> errors and logs; run any JavaScript against the page</td></tr>
      <tr><td><strong>Network</strong></td><td>Every request: method, status, headers, timing, size</td></tr>
      <tr><td><strong>Sources</strong></td><td>The page's code; set breakpoints, step through JS</td></tr>
      <tr><td><strong>Application</strong></td><td>Cookies and all storage types, inspectable and editable</td></tr>
      <tr><td><strong>Performance / Lighthouse</strong></td><td>Where rendering time goes; automated audits</td></tr>
    </tbody>
  </table>
</div>

<details class="article-exercise">
  <summary>🧪 Try it yourself</summary>

  <ol>
    <li>Open DevTools → <strong>Network</strong>, tick “Disable cache,” reload a news site. Count the requests; sort by size;
find the slowest one.</li>
    <li>In <strong>Elements</strong>, double-click any headline on any website and rewrite it. (Only your local copy changes;
refresh to restore.)</li>
    <li>In <strong>Console</strong>, run <code class="language-plaintext highlighter-rouge">document.title = "I control this page"</code> and watch the tab.</li>
    <li>In <strong>Application</strong>, inspect the cookies of a site you’re logged into; find the session cookie, note its
<code class="language-plaintext highlighter-rouge">HttpOnly</code> and <code class="language-plaintext highlighter-rouge">Secure</code> flags.</li>
    <li>Run a <strong>Lighthouse</strong> audit on your favorite site; most of its suggestions will now make sense.</li>
  </ol>

</details>

<details class="article-exercise">
  <summary>✅ Check yourself</summary>

  <details class="article-answer">
    <summary>Why does one page load involve dozens of <abbr title="HyperText Transfer Protocol">HTTP</abbr> requests?</summary>

    <p>The initial <abbr title="HyperText Markup Language">HTML</abbr> references many more resources (stylesheets, scripts, images, fonts), and scripts then
fetch <abbr title="Application Programming Interface">API</abbr> data of their own. Each of those is a separate <abbr title="HyperText Transfer Protocol">HTTP</abbr> request.</p>

  </details>

  <details class="article-answer">
    <summary>In the rendering pipeline, what's the difference between layout and paint?</summary>

    <p>Layout computes geometry: where every box sits and how big it is. Paint then fills in the actual pixels
inside that geometry: text, colors, borders, images.</p>

  </details>

  <details class="article-answer">
    <summary>Why does a <code>while(true) {}</code> loop freeze the whole page, including scrolling?</summary>

    <p>JavaScript shares the single main thread with rendering and event handling. While the loop runs, the browser
never gets the thread back to process input or draw new frames, so the page appears frozen.</p>

  </details>

  <details class="article-answer">
    <summary>Your login persists across browser restarts. Which storage mechanism is involved, and why that one?</summary>

    <p>A cookie with an expiry date. It survives restarts and, unlike localStorage, travels to the server with
every request automatically, which is what lets the server recognize your session.</p>

  </details>

</details>

<h2 class="article-section" id="the-whole-story-in-six-lines">The whole story, in six lines</h2>

<ol>
  <li>Machines find each other by <strong><abbr title="Internet Protocol">IP</abbr></strong> and exchange <strong>packets</strong> under shared <strong>protocols</strong>.</li>
  <li>On top of that, <strong><abbr title="HyperText Transfer Protocol">HTTP</abbr></strong> is the request/response language of the web; <strong><abbr title="HyperText Transfer Protocol Secure">HTTPS</abbr></strong> encrypts it.</li>
  <li><strong>Domain names</strong> give humans stable names, leased through registrars.</li>
  <li><strong>Hosting</strong> is where the site’s files or code actually live and answer requests.</li>
  <li><strong><abbr title="Domain Name System">DNS</abbr></strong> connects the name to the host: records, caches, and <abbr title="times to live">TTLs</abbr> in between.</li>
  <li>The <strong>browser</strong> ties it all together: fetch, render, run, store, inspect.</li>
</ol>

<p>Next time a page loads in 300 ms, you’ll know exactly how much machinery just cooperated to make that happen,
and next time something breaks, you’ll know which layer to interrogate first.</p>

<aside class="series-cta">
  <div class="series-cta__kicker">From packets to pixels &middot; Part 1</div>
  <p class="series-cta__text">
    This article is part of a hands-on series that rebuilds the web from first principles:
    the network first, then HTML, CSS, and JavaScript.
  </p>
  <div class="d-flex flex-wrap gap-2">
    <a class="btn text-white series-cta__btn" href="/from-packets-to-pixels/">
      All parts <i class="fa-solid fa-arrow-right"></i>
    </a>
    <button type="button" class="btn series-cta__subscribe-btn" data-bs-toggle="modal" data-bs-target="#seriesSubscribeModal">
      <i class="fa-solid fa-envelope"></i> Get new parts by email
    </button>
  </div>
</aside>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Everything that happens between typing a URL and seeing a page: packets, IP, TCP, ports, HTTP, HTTPS, domain names, hosting, DNS, and the browser rendering pipeline. One article, end to end, with hands-on exercises that need nothing but a terminal and a browser.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/from-packets-to-pixels.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/from-packets-to-pixels.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The one Arazzo condition no tool can evaluate safely</title><link href="https://vladimirgorej.com/blog/the-one-arazzo-condition-no-tool-can-evaluate-safely/" rel="alternate" type="text/html" title="The one Arazzo condition no tool can evaluate safely" /><published>2026-07-08T08:00:00+00:00</published><updated>2026-07-08T08:00:00+00:00</updated><id>https://vladimirgorej.com/blog/the-one-arazzo-condition-no-tool-can-evaluate-safely</id><content type="html" xml:base="https://vladimirgorej.com/blog/the-one-arazzo-condition-no-tool-can-evaluate-safely/"><![CDATA[<p class="lead">
  I'm building an Arazzo runner: a program that executes API workflows step by step. That means every step
  needs an answer to a deceptively simple question: did this step succeed?
</p>

<p>In the case I was implementing, that answer came from a <a href="https://spec.openapis.org/arazzo/v1.1.0.html#criterion-object">Criterion Object</a>
with a <code class="language-plaintext highlighter-rouge">simple</code> condition, such as <code class="language-plaintext highlighter-rouge">$statusCode == 200</code>. It looks obvious. It reads like plain English. But
if Arazzo workflows are meant to be executable and portable, then every runner has to read that condition
the same way.</p>

<p>So I wrote a quick evaluator. It handled the easy cases, then quietly fell apart on corner case after
corner case I hadn’t anticipated. That was enough to make me stop, put the runner down, and go into
research mode.</p>

<p>This is the story of what that turned up, and of how the detour became a standalone library,
<a href="https://github.com/swaggerexpert/arazzo-criterion">@swaggerexpert/arazzo-criterion</a>: a small evaluator
that reads each condition as faithfully as the spec permits, and is honest about the one case where no
tool can know for sure.</p>

<h2 id="what-a-condition-looks-like">What a condition looks like</h2>

<p><a href="https://spec.openapis.org/arazzo/v1.1.0.html">Arazzo</a> is the OpenAPI Initiative’s specification for
describing sequences of API calls: workflows. After each step, you check that it worked by evaluating its
success criteria. Each criterion is a <a href="https://spec.openapis.org/arazzo/v1.1.0.html#criterion-object">Criterion Object</a>,
and one common form is a <code class="language-plaintext highlighter-rouge">simple</code> condition:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">condition</span><span class="pi">:</span> <span class="s">$statusCode == 200</span></code></pre></figure>

<p>Read it out loud and it’s obvious: <em>did the server return a 200 status code?</em> That readability is the
whole appeal. You can write conditions that sound like what you mean:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">condition</span><span class="pi">:</span> <span class="s">$response.body.status == 'available' &amp;&amp; $response.body.pets[0].id &gt; 0</span></code></pre></figure>

<p><em>Is the status “available”, and does the first pet have a real id?</em> A person can read that. My job was to
write software that reads it the same way a person does, and, just as importantly, the same way every
<em>other</em> runner does.</p>

<p>That portability requirement is the part that matters. Arazzo isn’t prose documentation for humans; it’s a
machine-readable workflow description. If one runner treats a condition as true and another treats the same
condition as false, the workflow has stopped being portable. At that point the problem is no longer just
parsing. It’s interoperability.</p>

<h2 id="my-first-instinct-just-run-it">My first instinct: just run it</h2>

<p>My first idea was the lazy one: a condition looks like code. <code class="language-plaintext highlighter-rouge">$statusCode == 200</code> is almost a line you
could paste into a program and let the language run. Fill in the <code class="language-plaintext highlighter-rouge">$...</code> parts (like <code class="language-plaintext highlighter-rouge">$statusCode</code>) with
their real values, hand the rest to the language’s <code class="language-plaintext highlighter-rouge">eval</code>, and read back true or false. An afternoon’s work.</p>

<p>I’m glad I didn’t ship that. The obvious objection is safety: feeding arbitrary text into <code class="language-plaintext highlighter-rouge">eval</code> is the
classic way to run code you never meant to run.</p>

<p>So I reached for the safer version: parse the text with a proper JavaScript parser first (a well-tested
component that turns code into a structured tree without executing anything), then walk that tree myself
and decide what each piece means. No blind execution, no injection hole. This felt right for about a day.</p>

<p>Then the deeper problem surfaced, and it’s the reason this whole project exists. A <em>JavaScript</em> parser
reads <em>JavaScript’s</em> rules. A Python runner would reach for a Python parser, a Go one for Go’s, each with
its own subtly different semantics. I’d have traded “whatever <code class="language-plaintext highlighter-rouge">eval</code> does” for “whatever this one
language’s parser does”, still shackled to a single language, with no guarantee that two conformant runners
reach the same answer. Arazzo even layers on comparison rules that no language reproduces out of the box,
but I’ll come back to those.</p>

<p>That was the click: I didn’t need JavaScript semantics. I needed <em>Arazzo</em> semantics. A definition that
sits above any single language, that I could write down once and that every implementation, in any
language, could follow to the exact same result. That’s what pushed me toward describing the condition
language with its own precise, portable rulebook. And that’s where the real problem revealed itself.</p>

<h2 id="two-languages-one-line">Two languages, one line</h2>

<p>Writing that rulebook down sounds simple. It isn’t, and here is why: that one innocent line is secretly
<strong>two different mini-languages mixed together</strong>.</p>

<p>The first is for <em>pointing at data</em>: <code class="language-plaintext highlighter-rouge">$statusCode</code>, <code class="language-plaintext highlighter-rouge">$response.body</code>, <code class="language-plaintext highlighter-rouge">$request.query.limit</code>. Arazzo calls
these <a href="https://spec.openapis.org/arazzo/v1.1.0.html#runtime-expressions">runtime expressions</a>, and each
one just points at a value somewhere in the request or the response, saying <em>where to look</em>. They already
have a precise definition of their own. I’d formalized that definition earlier in a separate library,
<a href="https://github.com/swaggerexpert/arazzo-runtime-expression">@swaggerexpert/arazzo-runtime-expression</a>,
and building it out carefully even fed back into the standard: the work helped tighten the
runtime-expression grammar in Arazzo 1.1.0 (<a href="https://github.com/OAI/Arazzo-Specification/pull/454">PR #454</a>).
So my criterion library doesn’t reinvent any of that. It just reuses that parser.</p>

<p>The second is for <em>comparing</em> things: <code class="language-plaintext highlighter-rouge">==</code>, <code class="language-plaintext highlighter-rouge">!=</code>, <code class="language-plaintext highlighter-rouge">&lt;</code>, <code class="language-plaintext highlighter-rouge">&gt;</code>, the word-joiners <code class="language-plaintext highlighter-rouge">&amp;&amp;</code> and <code class="language-plaintext highlighter-rouge">||</code>, and so on.
This is the part that says <em>what to check</em> once you’ve looked.</p>

<p>In the common case I cared about, a condition is a runtime expression plus a comparison: <strong><code class="language-plaintext highlighter-rouge">⟨where to look⟩ ⟨what to check⟩</code></strong>.
Simple enough, until you notice that the two languages <strong>share some of the same symbols</strong>.</p>

<p>That’s the whole problem in one sentence. Let me show you why it bites.</p>

<h2 id="why-the-obvious-reading-breaks">Why the obvious reading breaks</h2>

<p>Runtime expressions in Arazzo are allowed to be surprisingly loose. A query-parameter name, for instance,
can legally contain almost any character, including <code class="language-plaintext highlighter-rouge">=</code>, <code class="language-plaintext highlighter-rouge">&lt;</code>, <code class="language-plaintext highlighter-rouge">&gt;</code>, even spaces. On its own that’s fine; a
runtime expression is usually the <em>only</em> thing on the line, so there’s nothing after it to get confused with.</p>

<p>Now put a runtime expression next to a comparison and watch it go wrong:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">condition</span><span class="pi">:</span> <span class="s">$request.query.limit == 10</span></code></pre></figure>

<p>You and I read that instantly: <em>the query parameter <code class="language-plaintext highlighter-rouge">limit</code> equals 10</em>. But the runtime-expression reader,
following its own loose rules, sees no reason to stop at <code class="language-plaintext highlighter-rouge">limit</code>. Spaces are allowed in a name. So is
<code class="language-plaintext highlighter-rouge">=</code>. So it happily swallows the <strong>entire line</strong>, <code class="language-plaintext highlighter-rouge">== 10</code> included, treating the whole thing as if the
parameter were literally named <code class="language-plaintext highlighter-rouge">"limit == 10"</code>. The comparison never gets its turn, and the reading
collapses.</p>

<figure class="figure d-block text-center my-4">
  <svg viewBox="0 0 760 300" role="img" aria-labelledby="splitTitle splitDesc" style="max-width:100%;height:auto;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;">
    <title id="splitTitle">Two ways to read one condition</title>
    <desc id="splitDesc">The condition dollar request dot query dot limit equals equals 10. The intended reading splits it into the runtime expression (dollar request dot query dot limit) and the comparison (equals equals 10). A literal reader instead swallows the whole line as a single runtime-expression name, losing the comparison.</desc>
    <text x="380" y="30" text-anchor="middle" font-size="15" fill="#6c757d" style="font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif">What you mean</text>
    <rect x="150" y="48" width="290" height="46" rx="8" fill="#e7f1ff" stroke="#0d6efd" stroke-width="1.5" />
    <text x="295" y="77" text-anchor="middle" font-size="18" fill="#084298">$request.query.limit</text>
    <line x1="452" y1="42" x2="452" y2="100" stroke="#198754" stroke-width="2.5" stroke-dasharray="4 3" />
    <rect x="464" y="48" width="146" height="46" rx="8" fill="#fff3e6" stroke="#fd7e14" stroke-width="1.5" />
    <text x="537" y="77" text-anchor="middle" font-size="18" fill="#a5510a">== 10</text>
    <text x="380" y="164" text-anchor="middle" font-size="15" fill="#6c757d" style="font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif">What a literal reader does</text>
    <rect x="150" y="182" width="460" height="46" rx="8" fill="#e7f1ff" stroke="#0d6efd" stroke-width="1.5" />
    <text x="380" y="211" text-anchor="middle" font-size="18" fill="#084298">$request.query.limit == 10</text>
    <text x="380" y="258" text-anchor="middle" font-size="14" fill="#dc3545" style="font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif">✗ swallowed as a single name, the comparison is lost</text>
    <rect x="238" y="278" width="13" height="13" rx="3" fill="#e7f1ff" stroke="#0d6efd" />
    <text x="257" y="289" font-size="13" fill="#495057" style="font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif">where to look</text>
    <rect x="408" y="278" width="13" height="13" rx="3" fill="#fff3e6" stroke="#fd7e14" />
    <text x="427" y="289" font-size="13" fill="#495057" style="font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif">what to check</text>
  </svg>
  <figcaption class="figure-caption">The same line, read two ways. The whole problem is deciding where the runtime expression stops.</figcaption>
</figure>

<p>It’s a bit like reading the sentence <em>“Call Mr. Green house painters today”</em> and not knowing whether
someone is named “Mr. Green” or “Mr. Green house.” Without a clear signal for where the name ends, a
literal-minded reader keeps going and gets it wrong.</p>

<h2 id="the-part-i-could-fix-and-the-part-i-couldnt">The part I could fix, and the part I couldn’t</h2>

<p>Some of these collisions have a clean answer. Take the dot, <code class="language-plaintext highlighter-rouge">.</code>, which shows up both inside runtime
expressions and as a way to reach into data. It sounds ambiguous, but it usually isn’t, because the
runtime-expression rulebook itself knows where a runtime expression like <code class="language-plaintext highlighter-rouge">$response.body</code> naturally ends. So I can simply <em>ask it</em>, and let the
rulebook draw the line rather than guessing. That collision, I could solve.</p>

<p>The unsolvable one is the shared <code class="language-plaintext highlighter-rouge">=</code>. Consider a query parameter that genuinely has an <code class="language-plaintext highlighter-rouge">=</code> in its name:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">condition</span><span class="pi">:</span> <span class="s">$request.query.a=b == 1</span></code></pre></figure>

<p>Is that <code class="language-plaintext highlighter-rouge">=</code> <strong>part of the parameter’s name</strong> (<code class="language-plaintext highlighter-rouge">a=b</code>), or the <strong>start of the comparison</strong> <code class="language-plaintext highlighter-rouge">==</code>? Both
readings are legal. There is no rule, none exists in the Arazzo spec, that says “this <code class="language-plaintext highlighter-rouge">=</code> is part of the
name” versus “this <code class="language-plaintext highlighter-rouge">=</code> is an operator.” A human can’t reliably tell either. Nobody can. The string is
simply ambiguous, and no amount of clever programming invents an answer that isn’t there.</p>

<p>This is the honest heart of it: <strong>the ambiguity isn’t a flaw in my code. It’s a property of what was
written.</strong> The best any implementation can do is be predictable about it and say so out loud.</p>

<h2 id="reading-a-condition-the-way-it-was-meant">Reading a condition the way it was meant</h2>

<p>So how do you read a condition the way its author meant it, while staying upfront about the cases you
can’t? Here’s the approach I shipped.</p>

<p>Instead of teaching my comparison-reader every rule of the runtime-expression language (copying someone
else’s rulebook and letting it drift out of date), I do something simpler. When I hit a runtime expression,
I grab the whole suspicious chunk and hand it to the one component that already knows every rule:
<a href="https://github.com/swaggerexpert/arazzo-runtime-expression">@swaggerexpert/arazzo-runtime-expression</a>,
the parser from earlier, used unmodified. I ask it a single question: <em>how much of this is a valid runtime
expression?</em></p>

<p>The parser reads as far as it legitimately can and hands back the leftover. Whatever’s left is the
“what to check” part. So for <code class="language-plaintext highlighter-rouge">$response.body.data[0].id</code>, it reports that <code class="language-plaintext highlighter-rouge">$response.body</code> is the
runtime expression, and I treat the rest, <code class="language-plaintext highlighter-rouge">.data[0].id</code>, as the criterion language’s navigation into the
resolved value: <em>reach into the first data item’s id</em>. The boundary is drawn by the component that actually
knows the rules, not by me guessing.</p>

<p>Two things fall out of this:</p>

<ul>
  <li><strong>The upside:</strong> I never fork or modify the runtime-expression rulebook. It stays the single source of
truth, and my library just <em>asks</em> it. When those rules improve upstream, I get the improvement for free.</li>
  <li><strong>The catch:</strong> my library first does a rough pass (“this looks like a runtime expression, roughly”) and
only then does the parser confirm it for real. Passing the rough pass isn’t enough; something can
<em>look</em> like a runtime expression and still be nonsense. Think of a bouncer glancing at you versus someone
actually checking your ID. That two-look approach, it turns out, is exactly how big specifications embed
one language inside another: match the rough shape first, verify it for real second.</li>
</ul>

<h2 id="the-one-case-that-stays-broken-on-purpose">The one case that stays broken (on purpose)</h2>

<p>In this <code class="language-plaintext highlighter-rouge">simple</code> grammar, because I stop the runtime-expression operand at the comparison symbols, that
operand can never <em>contain</em> those symbols. That’s the flip side of making the common case work:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">test</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@swaggerexpert/arazzo-criterion</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">test</span><span class="p">(</span><span class="dl">'</span><span class="s1">$statusCode == 200</span><span class="dl">'</span><span class="p">);</span>        <span class="c1">// true:  reads exactly as intended</span>
<span class="nx">test</span><span class="p">(</span><span class="dl">'</span><span class="s1">$request.query.a=b == 1</span><span class="dl">'</span><span class="p">);</span>   <span class="c1">// false: the '=' in the name can't be told apart</span>
<span class="nx">test</span><span class="p">(</span><span class="dl">'</span><span class="s1">$response.body#/a=b == 1</span><span class="dl">'</span><span class="p">);</span>  <span class="c1">// false: a pointer can't fence the '=' either</span></code></pre></figure>

<p>That middle line is the unavoidable casualty from earlier, and it fails <em>cleanly</em>. It doesn’t silently
guess wrong, which is the important part.</p>

<p>The last line is the one that looks like a way out. It is tempting to think the <code class="language-plaintext highlighter-rouge">#/…</code> pointer form acts as
a fence, with the <code class="language-plaintext highlighter-rouge">=</code> sitting safely <em>inside</em> the pointer. It doesn’t. A JSON Pointer has no closing
delimiter, and its tokens may contain <code class="language-plaintext highlighter-rouge">=</code> and even spaces. So the whole string <code class="language-plaintext highlighter-rouge">$response.body#/a=b == 1</code>
is also one valid runtime expression, with the pointer <code class="language-plaintext highlighter-rouge">/a=b == 1</code>. Same two readings, same refusal.</p>

<p>If you really need to reach a key like that, leave the <code class="language-plaintext highlighter-rouge">simple</code> type behind and use a <code class="language-plaintext highlighter-rouge">jsonpath</code> criterion.
JSONPath quotes its member names, so <code class="language-plaintext highlighter-rouge">$.pets[?@['a=b'] == 1]</code> means exactly one thing.</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr><th scope="col">Condition</th><th scope="col">How it reads</th><th scope="col">Result</th></tr>
    </thead>
    <tbody>
      <tr><td><code>$statusCode == 200</code></td><td>compare the status code to 200</td><td>works</td></tr>
      <tr><td><code>$request.query.limit == 10</code></td><td>compare query param <code>limit</code> to 10</td><td>works</td></tr>
      <tr><td><code>$request.query.a=b == 1</code></td><td>ambiguous: is <code>=</code> in the name, or the operator?</td><td>refused: ambiguous</td></tr>
      <tr><td><code>$response.body#/a=b == 1</code></td><td>no fence either: a pointer token may contain <code>=</code> and spaces</td><td>refused: ambiguous</td></tr>
    </tbody>
  </table>
</div>

<p>I didn’t bury this. It lives as a documented limitation,
<a href="https://github.com/swaggerexpert/arazzo-criterion/issues/3">issue #3</a>, with the reasoning spelled out.
If a future version of the Arazzo spec adds a way to “escape” these symbols, the limitation lifts, so I’ve
<a href="https://github.com/OAI/Arazzo-Specification/issues/518">asked it to</a>. Until then, being honest about it
<em>is</em> the feature.</p>

<h2 id="comparisons-play-by-arazzos-rules">Comparisons play by Arazzo’s rules</h2>

<p>So far I’ve shown <code class="language-plaintext highlighter-rouge">test</code>, which only answers “is this a valid condition?”. The point, though, is to
<code class="language-plaintext highlighter-rouge">evaluate</code> one and get an actual true or false. The library never fetches live data itself, so you hand it
a small <code class="language-plaintext highlighter-rouge">resolve</code> function that turns a runtime expression into its current value:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">import</span> <span class="p">{</span> <span class="nx">evaluate</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@swaggerexpert/arazzo-criterion</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">context</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">$statusCode</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">$response.body</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Available</span><span class="dl">'</span><span class="p">,</span> <span class="na">data</span><span class="p">:</span> <span class="p">[{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">42</span> <span class="p">}]</span> <span class="p">},</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">resolve</span> <span class="o">=</span> <span class="p">(</span><span class="nx">expression</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">context</span><span class="p">[</span><span class="nx">expression</span><span class="p">];</span>

<span class="nx">evaluate</span><span class="p">(</span><span class="dl">'</span><span class="s1">$statusCode == 200</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolve</span> <span class="p">});</span>                    <span class="c1">// true</span>
<span class="nx">evaluate</span><span class="p">(</span><span class="dl">"</span><span class="s2">$response.body.status == 'available'</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolve</span> <span class="p">});</span>  <span class="c1">// true, even though the data says "Available"</span>
<span class="nx">evaluate</span><span class="p">(</span><span class="dl">'</span><span class="s1">$response.body.data[0].id &gt; 10</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolve</span> <span class="p">});</span>        <span class="c1">// true</span></code></pre></figure>

<p>That second line hides the last decision worth mentioning, and it’s the loose end from the <code class="language-plaintext highlighter-rouge">eval</code> idea I
promised to come back to. When you compare things, the spec has its own gentle rules:
text comparisons ignore capitalization (<code class="language-plaintext highlighter-rouge">'Available'</code> equals <code class="language-plaintext highlighter-rouge">'available'</code>), the number <code class="language-plaintext highlighter-rouge">200</code> and the text
<code class="language-plaintext highlighter-rouge">'200'</code> are treated as equal, and a check passes on anything truthy and fails on <code class="language-plaintext highlighter-rouge">false</code>, nothing, or a
missing value. Those aren’t any programming language’s native rules, which is the <em>second</em> reason <code class="language-plaintext highlighter-rouge">eval</code>
would have quietly given wrong answers, even in a world with only one language. I implemented them
faithfully instead.</p>

<p>The tempting mistake is to import a rule from a <em>similar</em> feature next door. People often assume an empty
list should count as “false”, but that instinct comes from <em>other</em> condition types (the ones built on
JSONPath or XPath, where an empty match means failure). <code class="language-plaintext highlighter-rouge">simple</code> has no such rule. The library follows the
plain reading of the spec: a condition passes on any truthy value and fails only on <code class="language-plaintext highlighter-rouge">false</code>, <code class="language-plaintext highlighter-rouge">null</code>, or a
missing value. An empty list is none of those, so this evaluator treats it as truthy and passes it.</p>

<p>And that word “truthy” hides a real gap. The spec never defines whether an empty array is truthy, so the
answer falls to the host language, and languages disagree: <code class="language-plaintext highlighter-rouge">[]</code> is truthy in JavaScript but falsy in
Python (<code class="language-plaintext highlighter-rouge">bool([])</code> is <code class="language-plaintext highlighter-rouge">False</code>). This library runs on JavaScript, so <code class="language-plaintext highlighter-rouge">[]</code> passes; a Python runner could
fail the very same condition. It’s the ambiguous <code class="language-plaintext highlighter-rouge">=</code> all over again, just quieter: where the spec stays
silent, two conformant runners can still diverge. I’ve since opened
<a href="https://github.com/OAI/Arazzo-Specification/issues/517">an Arazzo spec issue</a> to pin these truthiness
rules down.</p>

<h2 id="why-this-matters-beyond-arazzo">Why this matters beyond Arazzo</h2>

<p>Strip away the specifics and you’re left with something you’ll recognize once you’ve seen it:</p>

<ul>
  <li>A web address sitting inside a sentence: where does the link end and the prose resume?</li>
  <li>A file name that’s allowed to contain spaces, listed next to other file names.</li>
  <li>Any place where you paste one kind of expression into the middle of another.</li>
</ul>

<p>The general truth is this: <strong>whenever you embed one little language inside another and they share the
same symbols, someone has to decide where one ends and the other begins, and if those symbols overlap
with no way to escape it, some inputs are simply undecidable.</strong> That’s not a bug you failed to
fix. It’s a fact about the input. Good engineering isn’t pretending the fact away; it’s choosing <em>where</em>
to make the tradeoff, making the common case feel effortless, and naming the rare case that
can’t work.</p>

<p>Arazzo’s <a href="https://spec.openapis.org/arazzo/v1.1.0.html#criterion-object"><code class="language-plaintext highlighter-rouge">simple</code> condition</a> is just a crisp, real example: a very permissive runtime-expression language, a
comparison language on top, a shared set of symbols, and a spec that offers no escape. So I made the
everyday conditions read exactly as intended, and I wrote down the one they can’t.</p>

<h2 id="for-the-curious-the-grammar">For the curious: the grammar</h2>

<p>Feel free to skip this part. But I promised a definition that lives above any single language, so here it
is. The Arazzo spec describes <code class="language-plaintext highlighter-rouge">simple</code> only in prose and never gives it a formal grammar, so this is an
original formalization: an <a href="https://www.rfc-editor.org/rfc/rfc5234">ABNF</a> grammar, the same portable
notation the internet’s specs are written in, that any implementation in any language can follow to the
same result.</p>

<details class="abnf-grammar">
  <summary>Show the full ABNF grammar</summary>

  <figure class="highlight"><pre><code class="language-text" data-lang="text">condition        = S logical-expr S

logical-expr     = logical-or-expr
logical-or-expr  = logical-and-expr *( S "||" S logical-and-expr )
logical-and-expr = basic-expr *( S "&amp;&amp;" S basic-expr )

basic-expr       = paren-expr / comparison-expr / test-expr
paren-expr       = [ logical-not-op S ] "(" S logical-expr S ")"
test-expr        = [ logical-not-op S ] comparable
comparison-expr  = comparable S comparison-op S comparable

logical-not-op   = "!"
comparison-op    = "==" / "!=" / "&lt;=" / "&gt;=" / "&lt;" / "&gt;"

comparable                 = literal / runtime-expression-operand
runtime-expression-operand = "$" 1*operand-char
operand-char               = %x22-25 / %x27 / %x2A-3B / %x3F-5A / %x5B-5D / %x5E-7A / %x7E / %x80-10FFFF

runtime-expression-navigation = 1*( member-access / index-access )
member-access                 = "." member-name
index-access                  = "[" index "]"
member-name                   = 1*( ALPHA / DIGIT / "_" / "-" )
index                         = 1*DIGIT

literal          = number / string / boolean / null
boolean          = "true" / "false"
null             = "null"

number           = ( int / "-0" ) [ frac ] [ exp ]
int              = "0" / ( [ "-" ] DIGIT1 *DIGIT )
frac             = "." 1*DIGIT
exp              = ( "e" / "E" ) [ "-" / "+" ] 1*DIGIT
DIGIT1           = %x31-39

string           = squote *( escaped-quote / string-char ) squote
escaped-quote    = squote squote
string-char      = %x20-26 / %x28-10FFFF
squote           = %x27

S                = *B
B                = %x20 / %x09 / %x0A / %x0D

ALPHA          = %x41-5A / %x61-7A
DIGIT          = %x30-39</code></pre></figure>

</details>

<p>Three things in there tie straight back to the story:</p>

<ul>
  <li><strong>The bounded token.</strong> <code class="language-plaintext highlighter-rouge">runtime-expression-operand = "$" 1*operand-char</code> is the “grab the whole <code class="language-plaintext highlighter-rouge">$…</code>
chunk” step. Look closely at <code class="language-plaintext highlighter-rouge">operand-char</code>: it is deliberately every character <em>except</em> whitespace and
the operator symbols <code class="language-plaintext highlighter-rouge">! &amp; ( ) &lt; = &gt; { | }</code>. That one exclusion is what makes a runtime expression stop
at the comparison, and it is the very same reason a runtime-expression operand can’t
<em>contain</em> those symbols. Both faces of the problem, one line of grammar.</li>
  <li><strong>Runtime expressions aren’t defined here.</strong> Notice the grammar never says what a valid runtime
expression actually <em>is</em>. It only captures the <code class="language-plaintext highlighter-rouge">$…</code> token, then delegates the real check to
arazzo-runtime-expression afterwards. The grammar matches the rough shape; the second pass confirms it.</li>
  <li><strong>The two-layer spine.</strong> The <code class="language-plaintext highlighter-rouge">logical-*</code> and <code class="language-plaintext highlighter-rouge">comparison-expr</code> rules are adapted from
<a href="https://www.rfc-editor.org/rfc/rfc9535">RFC 9535</a> (the JSONPath standard). A logical layer composes
booleans over a <em>flat</em>, non-recursive comparison layer, which is why a chained comparison like
<code class="language-plaintext highlighter-rouge">$a &lt; 200 &lt; 300</code> isn’t merely rejected, it’s ungrammatical: the rules give you no way to even write it.</li>
</ul>

<h2 id="try-it">Try it</h2>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>npm <span class="nb">install</span> @swaggerexpert/arazzo-criterion</code></pre></figure>

<div class="list-group mb-3">
  <a href="https://github.com/swaggerexpert/arazzo-criterion" class="list-group-item list-group-item-action" target="_blank" rel="noopener noreferrer">
    <div class="d-flex w-100 justify-content-between">
      <h2 class="h5 mb-1"><i class="fa-brands fa-github"></i> @swaggerexpert/arazzo-criterion</h2>
    </div>
    <blockquote class="blockquote fs-6 mb-1">
      Reads, validates, and evaluates the <em>simple</em> conditions of the Arazzo Criterion Object.
    </blockquote>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "SoftwareSourceCode",
        "author": { "@id": "https://vladimirgorej.com" },
        "name": "@swaggerexpert/arazzo-criterion",
        "abstract": "Reads, validates, and evaluates the simple conditions of the Arazzo Criterion Object.",
        "codeRepository": "https://github.com/swaggerexpert/arazzo-criterion"
      }
    </script>
  </a>
</div>

<p>It’s part of <a href="https://github.com/swaggerexpert">SwaggerExpert</a>, a family of libraries that formalize the
expression languages around OpenAPI and Arazzo:</p>

<ul>
  <li><a href="https://github.com/swaggerexpert/arazzo-runtime-expression" target="_blank" rel="noopener noreferrer">arazzo-runtime-expression</a> (the runtime-expression parser this library asks)</li>
  <li><a href="https://github.com/swaggerexpert/openapi-runtime-expression" target="_blank" rel="noopener noreferrer">openapi-runtime-expression</a> (its OpenAPI sibling)</li>
  <li><a href="https://github.com/swaggerexpert/jsonpath" target="_blank" rel="noopener noreferrer">jsonpath</a> (the query syntax that gives an escape hatch) and <a href="https://github.com/swaggerexpert/json-pointer" target="_blank" rel="noopener noreferrer">json-pointer</a></li>
</ul>

<h2 id="closing-words">Closing words</h2>

<p>I wanted one thing: software that reads a condition the way its author meant it. For everyday conditions,
it does. The trick is that it never guesses. It asks the runtime-expression parser where each expression
ends, and trusts the answer. One case stays out of reach: a name that contains a symbol like <code class="language-plaintext highlighter-rouge">=</code>, which
the spec gives no way to tell apart from the operator. No tool can evaluate that one safely, so rather than
guess, mine refuses it and says why. That is the condition in the title.</p>

<p>And it doesn’t end here. <code class="language-plaintext highlighter-rouge">simple</code> still has no formal grammar in the spec, and its evaluation rules don’t
say how a bare value like an empty array should be treated. So I’ve taken both questions back to the Arazzo
specification: one issue to <a href="https://github.com/OAI/Arazzo-Specification/issues/517">define the evaluation semantics</a>,
and one to <a href="https://github.com/OAI/Arazzo-Specification/issues/518">define a normative grammar and settle the operand boundary</a>.
Some of it may land in a future version of the spec. That is already how the earlier runtime-expression
fixes reached Arazzo 1.1.0, and it is where a definition meant to be shared belongs.</p>

<h2 id="update-21092026">Update (21.09.2026)</h2>

<p>An earlier version of this article said that <code class="language-plaintext highlighter-rouge">$response.body#/a=b == 1</code> works, because the JSON Pointer
form “has its own clear fences”. That was wrong. No published version of the library accepts it: <code class="language-plaintext highlighter-rouge">test()</code> returns
<code class="language-plaintext highlighter-rouge">false</code>. A JSON Pointer has no terminator, so the pointer form is exactly as ambiguous as the query
parameter form. I’ve corrected the code sample, the table, and the text around them, and pointed to a
<code class="language-plaintext highlighter-rouge">jsonpath</code> criterion as the real way out.</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Arazzo simple conditions look like plain comparisons, but they combine runtime expressions with their own comparison language. Most cases can be parsed faithfully. One case cannot. This is how @swaggerexpert/arazzo-criterion handles the boundary without pretending ambiguity away.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/arazzo-criterion.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/arazzo-criterion.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">‘Off Spec’ № 01: On OpenAPI, agents, and what specs are actually for</title><link href="https://vladimirgorej.com/blog/off-spec-01-openapi-agents-and-what-specs-are-actually-for/" rel="alternate" type="text/html" title="‘Off Spec’ № 01: On OpenAPI, agents, and what specs are actually for" /><published>2026-07-03T08:00:00+00:00</published><updated>2026-07-03T08:00:00+00:00</updated><id>https://vladimirgorej.com/blog/off-spec-01-openapi-agents-and-what-specs-are-actually-for</id><content type="html" xml:base="https://vladimirgorej.com/blog/off-spec-01-openapi-agents-and-what-specs-are-actually-for/"><![CDATA[<p class="lead">
  <em>'Off Spec'</em>, brought to you by APIwiz, is a conversation series about how APIs actually get built,
  maintained, governed, and consumed at scale — what the specs don't capture. For № 01,
  <a href="https://www.linkedin.com/in/shane-o-connor-apiwiz/" target="_blank" rel="noopener noreferrer">Shane O'Connor</a>
  put fifteen questions to me. This is a re-post of the
  <a href="https://www.linkedin.com/pulse/off-spec-01-vladim%C3%ADr-gorej-char0n-openapi-agents-what-shane-o-connor-yw5ge/" target="_blank" rel="noopener noreferrer">original interview</a>.
</p>

<p>I spent five years as a core maintainer of Swagger tools. Before that I was principal engineer at Apiary, home of
API Blueprint. I’m a maintainer of the AsyncAPI specification. And today I co-found SpecLynx and work as an AI
engineer at Jentic, where the two ends of my career — specifications and agents — are finally colliding.</p>

<nav class="interview-toc">
  <div class="interview-toc__title">Jump to a question</div>
  <ol>
    <li><a href="#q1">What no spec will ever capture</a></li>
    <li><a href="#q2">What actually killed API Blueprint</a></li>
    <li><a href="#q3">The decision almost nobody noticed</a></li>
    <li><a href="#q4">Was OpenAPI 3.1 worth the pain?</a></li>
    <li><a href="#q5">API wisdom that's just wrong</a></li>
    <li><a href="#q6">What teams miss about specs</a></li>
    <li><a href="#q7">One feature I'd delete</a></li>
    <li><a href="#q8">Why event-driven feels second-class</a></li>
    <li><a href="#q9">Was OpenAPI written for agents?</a></li>
    <li><a href="#q10">Will hand-crafted specs get bypassed?</a></li>
    <li><a href="#q11">MCP: reinventing OpenAPI?</a></li>
    <li><a href="#q12">What I built that the world didn't want</a></li>
    <li><a href="#q13">The unglamorous plumbing that matters</a></li>
    <li><a href="#q14">Who actually pays for open source</a></li>
    <li><a href="#q15">Lightning round</a></li>
  </ol>
</nav>

<h2 id="q1" class="interview-q h4"><span class="interview-q__num">1.</span> The series is called Off Spec. So let's start there: what's something about how APIs actually get built and consumed that no specification will ever capture?</h2>

<p>No specification fully captures the original author’s intent.</p>

<p>A spec can describe paths, schemas, status codes, and examples, but it rarely explains why the API was designed
that way: the trade-offs, constraints, assumptions, and consumer needs behind it.</p>

<p>That intent matters. Without it, people can follow the contract and still miss the design.</p>

<p class="interview-pullquote">The real API is not just the spec. It is the spec plus the documentation, SDKs, examples, error messages, and production behavior people come to rely on.</p>

<h2 id="q2" class="interview-q h4"><span class="interview-q__num">2.</span> You were principal engineer at Apiary, home of API Blueprint, a spec that genuinely competed with Swagger. What actually killed Blueprint, and what did OpenAPI get right that it didn't?</h2>

<p>Blueprint was not killed by one thing. It was a mix of tooling economics, industry convergence, and probably
corporate gravity after Apiary.</p>

<p>API Blueprint had a beautiful authoring experience. It was readable, elegant, and very human-friendly. But
OpenAPI was more explicit and easier for a broad tooling ecosystem to build around: generators, validators,
gateways, documentation tools, and enterprise platforms.</p>

<p>Once the industry started converging on OpenAPI, even Apiary had to support it. At that point Blueprint was no
longer just competing on specification quality; it was competing against a network effect.</p>

<p>So I would not say Blueprint lost because it was bad. It lost because OpenAPI became the safer ecosystem bet, and
after the Apiary/Oracle chapter there was not enough independent momentum left to reverse that.</p>

<h2 id="q3" class="interview-q h4"><span class="interview-q__num">3.</span> Five years maintaining Swagger tools that millions of developers depend on. What's the most consequential decision you made that almost nobody noticed?</h2>

<p>Probably Swagger ApiDOM.</p>

<p>We built Swagger ApiDOM as a semantic parser for API specifications, a unified way to parse, process, transform,
and reason about specs across the Swagger ecosystem. The important part is that we integrated it in a completely
backward-compatible way.</p>

<p>For most users, the only visible effect was that OpenAPI 3.1 support arrived. But underneath, it changed the
foundation: Swagger tooling no longer had to treat every spec version and format as a separate world. It also
opened the door to supporting AsyncAPI, Arazzo, and Overlays through the same underlying model.</p>

<p class="interview-pullquote">Good infrastructure becomes invisible when it works well.</p>

<h2 id="q4" class="interview-q h4"><span class="interview-q__num">4.</span> OpenAPI 3.1 finally aligned with JSON Schema 2020-12 after years of divergence, and you built the renderer for it. Was that alignment worth the pain, or did it fix a problem most users never actually had?</h2>

<p>Yes, it was worth the pain.</p>

<p>Most real-world specifications were still OpenAPI 3.0.x, but there was clear interest in OpenAPI 3.1 support. I do
not think most users cared about JSON Schema 2020-12 alignment as a standards debate. They cared that newer
specifications worked, validation was more consistent, and tooling could support OpenAPI 3.1 properly.</p>

<p>For tooling authors, the old divergence between OpenAPI Schema Objects and JSON Schema created years of special
cases across renderers, validators, parsers, and generators. OpenAPI 3.1 did not remove all complexity, but it did
move the ecosystem closer to a shared foundation.</p>

<p>So yes, it fixed a real problem, just not always at the layer where most users described it.</p>

<h2 id="q5" class="interview-q h4"><span class="interview-q__num">5.</span> What's a piece of received wisdom in the API world that you think is just wrong? "Design-first always," "REST is dead," "spec-driven everything." Get as spicy as you want.</h2>

<p>The received wisdom I think is wrong is that specs are mainly for documentation.</p>

<p>That view is too small. In today’s world, an API specification should be treated more like infrastructure-as-code:
a living description of the system that is versioned, tested, checked against reality, and kept up to date.</p>

<p>A spec should participate in the lifecycle of the API: design, review, validation, testing, governance, SDK
generation, agent consumption, and runtime checks.</p>

<p class="interview-pullquote">The future is not "write a spec to produce nice documentation." The future is "use the spec to prove the API, the tooling, and the consumers still agree."</p>

<h2 id="q6" class="interview-q h4"><span class="interview-q__num">6.</span> Most teams treat the OpenAPI spec as a documentation output, something you generate at the end. What are they fundamentally missing about what a spec is for?</h2>

<p>They are missing the feedback loop.</p>

<p>If the spec is generated only at the end, it can tell consumers what the API does, but it cannot help the team
catch problems while the API is still being shaped.</p>

<p>A good spec should be part of the pull request, the CI pipeline, and the release process. It should catch breaking
changes, validate examples, drive contract tests, generate SDKs, and give consumers something stable to review and
build against as the API evolves.</p>

<p>Documentation is one output of a spec. The bigger job is keeping design, implementation, tooling, and consumers
aligned.</p>

<h2 id="q7" class="interview-q h4"><span class="interview-q__num">7.</span> If you could delete one feature from the OpenAPI spec, and one thing from your own past code, what goes, and who'd be mad about it?</h2>

<p>From OpenAPI 3.1, I would probably delete the jsonSchemaDialect fixed field.</p>

<p>Most people think of OpenAPI 3.1 as “the version that aligned with JSON Schema 2020-12.” But technically, through
jsonSchemaDialect, an OpenAPI document can declare a different JSON Schema dialect, including previous drafts or
even a custom dialect.</p>

<p>In theory, that flexibility makes sense. In practice, I have not seen many OpenAPI documents that actually use it.
The complexity it brings to tooling implementation feels disproportionate. Parsers, validators, renderers, and
generators suddenly have to care not just about OpenAPI 3.1, but about which JSON Schema dialect is active and what
behavior that implies.</p>

<p>From my own past code, I would happily delete the code that tried to provide full support for jsonSchemaDialect.
It was the kind of implementation work where the theoretical correctness was real, but the practical value for most
users was hard to justify.</p>

<p>Who would be mad? Probably the very small number of people who really need custom dialect support. But I suspect
most users would happily trade that flexibility for simpler, more predictable tooling.</p>

<h2 id="q8" class="interview-q h4"><span class="interview-q__num">8.</span> You also maintain AsyncAPI. Why does event-driven still feel like a second-class citizen in the API tooling world, and is that finally changing?</h2>

<p>Event-driven still feels second-class because it is harder to see.</p>

<p>With HTTP APIs, the interaction model is familiar: request, response, status code, payload. It maps naturally to
documentation, testing, gateways, SDKs, and mental models. Event-driven systems are different. The important
behavior is spread across producers, consumers, brokers, topics, retries, ordering, delivery guarantees, and time.</p>

<p>That makes the tooling problem harder. You are not just describing an endpoint; you are describing a system of
conversations that may happen asynchronously and indirectly.</p>

<p>I do think it is changing. AsyncAPI gives the industry a shared language for describing those systems, and as
workflows, agents, and event-driven architectures become more common, that language becomes more important.</p>

<h2 id="q9" class="interview-q h4"><span class="interview-q__num">9.</span> You're now an AI engineer at Jentic. LLMs and agents have quietly become the biggest consumers of API specs. Was OpenAPI ever written for machines, or for humans pretending to be machines?</h2>

<p>OpenAPI was written for machines, but not for agents.</p>

<p>It was designed so tooling could parse an API contract and generate documentation, clients, servers, validators,
tests, and governance checks. In that sense, it was absolutely machine-readable by design.</p>

<p>But agents are a different kind of consumer. They do not just need a valid contract or a generated client. They
need to decide whether an operation is relevant, what the side effects might be, how to combine calls, how to
recover from errors, and whether the API is safe to use in a given context.</p>

<p>That is where work like Jentic API Scorecard comes in: measuring whether an OpenAPI document is actually aligned
with how agents use specs.</p>

<p class="interview-pullquote">The question is no longer just "is this valid OpenAPI?" It is "does this spec make the API understandable, usable, and safe for an autonomous consumer?"</p>

<h2 id="q10" class="interview-q h4"><span class="interview-q__num">10.</span> Honest take: are we about to watch a generation of lovingly hand-crafted API specs get bypassed, because an agent would rather read the code or just hit the endpoint and see what happens?</h2>

<p>I think agents will bypass bad specs, not good ones.</p>

<p>If a specification is outdated, incomplete, or just a thin documentation artifact, then yes, an agent may get more
value from reading code, examples, traffic, or trying the endpoint directly. In that world, the lovingly
hand-crafted spec risks becoming decorative.</p>

<p>But a good OpenAPI document is still one of the best ways to give an agent a structured, intentional view of an
API. Code tells you what is implemented. Runtime behavior tells you what happens. A spec should tell you what is
intended, supported, stable, and safe to rely on.</p>

<p>So I do not think agents make specs irrelevant. I think they raise the bar.</p>

<p class="interview-pullquote">Specs that are stale will be ignored faster. Specs that are accurate, rich, and aligned with reality become even more important.</p>

<h2 id="q11" class="interview-q h4"><span class="interview-q__num">11.</span> MCP and tool schemas are suddenly everywhere. Is that the API world reinventing OpenAPI badly, or solving something OpenAPI genuinely couldn't?</h2>

<p>I do not think MCP and tool schemas are just OpenAPI reinvented badly. They are solving a different problem.</p>

<p>OpenAPI describes an API surface in detail: operations, parameters, schemas, responses, auth, errors, and
documentation. MCP is more focused on exposing capabilities to agents as callable tools.</p>

<p>That narrower focus is useful. An agent does not always need the full API description; it needs to know what action
it can take, what inputs are required, what result to expect, and whether the action is safe in context.</p>

<p>But I do think there is a risk of relearning old API lessons. Versioning, authentication, error handling,
pagination, compatibility, and governance all come back eventually.</p>

<p>So my take is: MCP is not a replacement for OpenAPI. It is an agent-facing runtime layer. The interesting future is
connecting them: using OpenAPI for rich API context, and MCP/tool schemas for the operational interface agents
actually call.</p>

<h2 id="q12" class="interview-q h4"><span class="interview-q__num">12.</span> You founded SpecLynx and re-built ApiDOM. Serious bets. What's something you poured yourself into that the world just didn't want, and were they right not to want it?</h2>

<p>SpecLynx is probably the clearest example.</p>

<p>We built a VS Code extension and editor around API specifications, and I still believe the problem was real. At the
time, a better human editing experience for specs made sense.</p>

<p>But the world has evolved. The more important surfaces now are MCP, LSP, CLIs, CI pipelines, and agent workflows,
places where specifications are not just written and viewed, but actively used, checked, transformed, and connected
to other systems.</p>

<p>Were people right not to want it? In that form, probably yes. Not because the idea was wrong, but because the
interface the world needs has changed. The future is less about people lovingly editing specs by hand, and more
about tools and agents continuously working with them.</p>

<h2 id="q13" class="interview-q h4"><span class="interview-q__num">13.</span> What's the least glamorous part of API tooling, the unsexy plumbing, that actually matters more than all the design-philosophy debates people love to have on Twitter?</h2>

<p>Proper parsing and reference resolution.</p>

<p>They are not glamorous, but they are where API tooling either becomes trustworthy or quietly falls apart. People
often think parsing means “read some JSON or YAML,” and reference resolution means “follow a <code class="language-plaintext highlighter-rouge">$ref</code>.” In reality,
both are much harder.</p>

<p>Good tooling has to understand the specification version, preserve semantic meaning, track source locations, handle
malformed input, and resolve references across files, URLs, anchors, bundles, dialects, and all the edge cases real
users create.</p>

<p>Everything else depends on that foundation: validation, rendering, editing, linting, code generation, governance,
testing, and now agent consumption.</p>

<p class="interview-pullquote">If parsing or resolution is wrong, every layer above it becomes unreliable.</p>

<h2 id="q14" class="interview-q h4"><span class="interview-q__num">14.</span> Open source at Swagger's scale: what's the honest story about who actually pays for the tooling the entire API industry quietly runs on?</h2>

<p>The honest story is that open source at this scale is often paid for indirectly, but it is paid for.</p>

<p>In the Swagger ecosystem, SmartBear played a central role. It owned the Swagger brand, built commercial products
around it, and funded engineering work on open source tooling that mattered both to those products and to the wider
API community.</p>

<p>That model has real benefits. The ecosystem gets dedicated engineering time, and the company has a strategic reason
to invest. But it also creates tension: the tooling is used far beyond one company, while the responsibility for
maintaining it is not shared equally.</p>

<p>So the honest answer is that some of the work is funded by companies with a reason to invest, and some of it is
still carried by maintainers because the ecosystem depends on it.</p>

<h2 id="q15" class="interview-q h4"><span class="interview-q__num">15.</span> Lightning round. One word or one line each.</h2>

<dl class="interview-lightning row">
  <dt class="col-sm-4">Design-first or code-first?</dt>
  <dd class="col-sm-8">Contract-first.</dd>

  <dt class="col-sm-4">OpenAPI or GraphQL schema?</dt>
  <dd class="col-sm-8">OpenAPI for action-oriented APIs; GraphQL schema when clients need to query related data flexibly.</dd>

  <dt class="col-sm-4">Most overrated API tool?</dt>
  <dd class="col-sm-8">Static API portals.</dd>

  <dt class="col-sm-4">Most underrated?</dt>
  <dd class="col-sm-8">A good parser.</dd>

  <dt class="col-sm-4">Will REST still dominate in 2030?</dt>
  <dd class="col-sm-8">Yes, but agents will care more about clear operations, good descriptions, predictable errors, and safety than whether the API is perfectly RESTful.</dd>
</dl>

<hr />

<p class="text-muted">
  This interview originally appeared on LinkedIn as part of APIwiz's <em>'Off Spec'</em> series. Read the
  <a href="https://www.linkedin.com/pulse/off-spec-01-vladim%C3%ADr-gorej-char0n-openapi-agents-what-shane-o-connor-yw5ge/" target="_blank" rel="noopener noreferrer">original article</a>.
</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[In the first edition of APIwiz's 'Off Spec' series, Shane O'Connor put fifteen questions to Vladimír Gorej about how APIs actually get built, maintained, and consumed at scale — from what killed API Blueprint, to why OpenAPI was written for machines but not for agents, to what specs are really for once machines start reading them instead of humans.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/off-spec-01.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/off-spec-01.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Advanced memoization for JavaScript functions with lodash.memoize</title><link href="https://vladimirgorej.com/blog/advanced-memoization-for-javascript-functions-with-lodash-memoize/" rel="alternate" type="text/html" title="Advanced memoization for JavaScript functions with lodash.memoize" /><published>2022-02-05T18:00:00+00:00</published><updated>2022-02-05T18:00:00+00:00</updated><id>https://vladimirgorej.com/blog/advanced-memoization-for-javascript-functions-with-lodash-memoize</id><content type="html" xml:base="https://vladimirgorej.com/blog/advanced-memoization-for-javascript-functions-with-lodash-memoize/"><![CDATA[<blockquote class="blockquote lead">
  <p>
    "In computing, memoization or memoisation is an optimization technique used primarily to speed up 
    computer programs by storing the results of expensive function calls and returning the cached result
    when the same inputs occur again."
  </p>
  <footer class="blockquote-footer"><cite title="Wikipedia on Memoization">Wikipedia on Memoization</cite></footer>
</blockquote>

<p><a href="https://lodash.com/" target="_blank" rel="noopener noreferrer">Lodash</a> is a modern JavaScript utility library delivering modularity, performance &amp; extras.
It’s a ubiquitous library, and your project probably uses it, even if you’re not aware of it.
Lodash contains an implementation of memoization in the form of <a href="https://lodash.com/docs/4.17.15#memoize" target="_blank" rel="noopener noreferrer">memoize</a> function.</p>

<p>By default, memoize function only uses the first argument provided to the memoized function as the cache key. 
Here is the simplest possible example of memoization:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="p">{</span> <span class="nx">memoize</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="s2">`My name is </span><span class="p">${</span><span class="nx">arg1</span><span class="p">}</span><span class="s2">`</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">func</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; 'My name is John'</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; 'My name is John' (cache hit)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 1</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>It’s important to realize that the cache key of the memoized function is determined using <a href="https://tc39.es/ecma262/multipage/abstract-operations.html#sec-samevaluezero" target="_blank" rel="noopener noreferrer">SameValueZero</a> comparison:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="p">{</span> <span class="nx">memoize</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">arg1</span><span class="p">)</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">func</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">({</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">}));</span> <span class="c1">// =&gt; '{"a":1}'</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">({</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">}));</span> <span class="c1">// =&gt; '{"a":1}'</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 2</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Now, what if we introduce more arguments to our func?</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="p">{</span> <span class="nx">memoize</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="s2">`</span><span class="p">${</span><span class="nx">arg1</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">arg2</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">func</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Doe</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; "John Doe"</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">FooBar</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; "John Doe" (cache hit)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 2</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>We can see that there is an obvious problem. As stated above, lodash only uses the first argument
as the cache key. To fix this, we have to use resolver function:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="p">{</span> <span class="nx">memoize</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="s2">`</span><span class="p">${</span><span class="nx">arg1</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">arg2</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">resolver</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">([</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">]);</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="nx">resolver</span><span class="p">);</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Doe</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; "John Doe"</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="dl">'</span><span class="s1">John</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">FooBar</span><span class="dl">'</span><span class="p">));</span> <span class="c1">// =&gt; "John FooBar"</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 2</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>This resolver solution obviously wouldn’t scale. What if our func needs to consume complex objects as arguments?
That’s fine unless the object arguments are huge or contain a cyclic reference.</p>

<p>When arguments are huge objects, we’ll spend too much CPU time serializing those objects into JSON strings 
to determine the cache key. And instead of the SameValueZero comparison for objects, we’ll be comparing huge strings.
When object arguments contain a cyclic reference, we’ll not be able to serialize them into JSON string and determine the cache key.</p>

<p>Lodash has one remaining trick up its sleeve to overcome these two problems - being able to influence how 
cache keys are maintained. To achieve our goal, we’ll have to create a specialization of <code class="language-plaintext highlighter-rouge">lodash.memoize</code>;
the function called <code class="language-plaintext highlighter-rouge">memoizeN</code>:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="p">{</span> <span class="nx">memoize</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">lodash</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">sameValueZero</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span> <span class="o">||</span> <span class="p">(</span><span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">y</span><span class="p">));</span>
<span class="kd">const</span> <span class="nx">shallowArrayEquals</span> <span class="o">=</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
    <span class="o">&amp;&amp;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="nx">b</span><span class="p">.</span><span class="nx">length</span>
    <span class="o">&amp;&amp;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">every</span><span class="p">((</span><span class="nx">val</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">sameValueZero</span><span class="p">(</span><span class="nx">val</span><span class="p">,</span> <span class="nx">b</span><span class="p">[</span><span class="nx">index</span><span class="p">]));</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">args</span><span class="p">;</span>

<span class="kd">class</span> <span class="nx">Cache</span> <span class="kd">extends</span> <span class="nb">Map</span> <span class="p">{</span>
  <span class="k">delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="kd">const</span> <span class="nx">foundKey</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">));</span>
    <span class="k">return</span> <span class="k">super</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">foundKey</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="kd">get</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="kd">const</span> <span class="nx">foundKey</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">));</span>
    <span class="k">return</span> <span class="k">super</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">foundKey</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nx">has</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="k">return</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">findIndex</span><span class="p">(</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">memoizeN</span> <span class="o">=</span> <span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolver</span> <span class="o">=</span> <span class="nx">list</span> <span class="p">}</span> <span class="o">=</span> <span class="p">{})</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="na">Cache</span><span class="p">:</span> <span class="nx">OriginalCache</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">;</span>
  <span class="nx">memoize</span><span class="p">.</span><span class="nx">Cache</span> <span class="o">=</span> <span class="nx">Cache</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">memoized</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">resolver</span><span class="p">);</span>
  <span class="nx">memoize</span><span class="p">.</span><span class="nx">Cache</span> <span class="o">=</span> <span class="nx">OriginalCache</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">memoized</span><span class="p">;</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>The real trick here is that the memoized function parameters are transformed into a list, and this list forms a cache key.
We override the <code class="language-plaintext highlighter-rouge">lodash.memoize.Cache</code> class on an ad-hoc basis with our implementation capable of doing cache key
lookups for our list cache keys.</p>

<p>Observe the following example:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">complexObj</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">};</span>
<span class="kd">const</span> <span class="nx">simpleObj</span> <span class="o">=</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">};</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">arg2</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">resolver</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">arg2</span><span class="p">)];</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoizeN</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolver</span> <span class="p">});</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">complexObj</span><span class="p">,</span> <span class="nx">simpleObj</span><span class="p">));</span> <span class="c1">// =&gt; {b: 2}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">complexObj</span><span class="p">,</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}));</span> <span class="c1">// =&gt; {b: 2} (cache hit)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">({</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}));</span> <span class="c1">// =&gt; {b: 2}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 2</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>We intend to memoize func using both arguments to form the cache key. For the first argument,
use the SameValueZero comparison on the original value. For the second one, use SameValueZero comparison
on JSON stringified value.</p>

<p>We can go even further with the specialization to make <code class="language-plaintext highlighter-rouge">memoizeN</code> consume a list of comparator functions
applied to appropriate arguments instead of abusing a resolver to serialize some arguments.</p>

<p>The implementation will look as follows:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">memoize</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash/memoize</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isEqual</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">lodash/isEqual</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">sameValueZero</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span> <span class="o">||</span> <span class="p">(</span><span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">y</span><span class="p">));</span>
<span class="kd">const</span> <span class="nx">makeShallowArrayEquals</span> <span class="o">=</span> <span class="p">(</span><span class="nx">comparators</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
    <span class="o">&amp;&amp;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">length</span> <span class="o">===</span> <span class="nx">b</span><span class="p">.</span><span class="nx">length</span>
    <span class="o">&amp;&amp;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">every</span><span class="p">((</span><span class="nx">val</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">comparator</span> <span class="o">=</span> <span class="k">typeof</span> <span class="nx">comparators</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">function</span><span class="dl">'</span> <span class="p">?</span> <span class="nx">comparators</span> <span class="p">:</span> <span class="nx">comparators</span><span class="p">[</span><span class="nx">index</span><span class="p">]</span> <span class="o">||</span> <span class="nx">sameValueZero</span><span class="p">;</span>
      <span class="k">return</span> <span class="nx">comparator</span><span class="p">(</span><span class="nx">val</span><span class="p">,</span> <span class="nx">b</span><span class="p">[</span><span class="nx">index</span><span class="p">]);</span>
    <span class="p">});</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">args</span><span class="p">;</span>

<span class="kd">class</span> <span class="nx">Cache</span> <span class="kd">extends</span> <span class="nb">Map</span> <span class="p">{</span>
  <span class="nx">shallowArrayEquals</span> <span class="o">=</span> <span class="nx">makeShallowArrayEquals</span><span class="p">(</span><span class="nx">sameValueZero</span><span class="p">);</span>

  <span class="k">delete</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="kd">const</span> <span class="nx">foundKey</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">));</span>
    <span class="k">return</span> <span class="k">super</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">foundKey</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="kd">get</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="kd">const</span> <span class="nx">foundKey</span> <span class="o">=</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">));</span>
    <span class="k">return</span> <span class="k">super</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">foundKey</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nx">has</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">keys</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">keys</span><span class="p">());</span>
    <span class="k">return</span> <span class="nx">keys</span><span class="p">.</span><span class="nx">findIndex</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">shallowArrayEquals</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">memoizeN</span> <span class="o">=</span> <span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="p">{</span> <span class="nx">resolver</span> <span class="o">=</span> <span class="nx">list</span><span class="p">,</span> <span class="nx">comparators</span> <span class="o">=</span> <span class="nx">sameValueZero</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="na">Cache</span><span class="p">:</span> <span class="nx">OriginalCache</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">;</span>
  <span class="nx">memoize</span><span class="p">.</span><span class="nx">Cache</span> <span class="o">=</span> <span class="nx">Cache</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">memoized</span> <span class="o">=</span> <span class="nx">memoize</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">resolver</span> <span class="o">||</span> <span class="nx">list</span><span class="p">);</span>
  <span class="nx">memoized</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">shallowArrayEquals</span> <span class="o">=</span> <span class="nx">makeShallowArrayEquals</span><span class="p">(</span><span class="nx">comparators</span><span class="p">);</span>
  <span class="nx">memoize</span><span class="p">.</span><span class="nx">Cache</span> <span class="o">=</span> <span class="nx">OriginalCache</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">memoized</span><span class="p">;</span>
<span class="p">};</span>

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nx">memoizeN</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>This version of memoizeN applies a specific comparator algorithm for each argument:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">};</span>
<span class="kd">const</span> <span class="nx">obj2</span> <span class="o">=</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">};</span>
<span class="kd">const</span> <span class="nx">obj3</span> <span class="o">=</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="mi">3</span><span class="p">};</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">arg3</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">strictEquality</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">sameValueZero</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span> <span class="o">||</span> <span class="p">(</span><span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">y</span><span class="p">));</span>
<span class="kd">const</span> <span class="nx">deepEquality</span> <span class="o">=</span> <span class="nx">lodash</span><span class="p">.</span><span class="nx">isEqual</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">comparators</span> <span class="o">=</span> <span class="p">[</span><span class="nx">strictEquality</span><span class="p">,</span> <span class="nx">sameValueZero</span><span class="p">,</span> <span class="nx">deepEquality</span><span class="p">];</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoizeN</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">{</span> <span class="nx">comparators</span> <span class="p">});</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">obj1</span><span class="p">,</span> <span class="nx">obj2</span><span class="p">,</span> <span class="nx">obj3</span><span class="p">));</span> <span class="c1">// =&gt; {c: 3}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">obj1</span><span class="p">,</span> <span class="nx">obj2</span><span class="p">,</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="mi">3</span><span class="p">}));</span> <span class="c1">// =&gt; {c: 3} (cache hit)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">obj1</span><span class="p">,</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">},</span> <span class="nx">obj3</span><span class="p">);</span> <span class="c1">// =&gt; {c: 3}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 2</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>If we define <code class="language-plaintext highlighter-rouge">comparators</code> option as a function, the memoized function will use it to compare every argument. 
When comparators are not provided, SameValueZero comparison is used for all arguments.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">};</span>
<span class="kd">const</span> <span class="nx">obj2</span> <span class="o">=</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">};</span>
<span class="kd">const</span> <span class="nx">obj3</span> <span class="o">=</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="mi">3</span><span class="p">};</span>

<span class="kd">const</span> <span class="nx">func</span> <span class="o">=</span> <span class="p">(</span><span class="nx">arg1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">arg3</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">sameValueZero</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span> <span class="o">||</span> <span class="p">(</span><span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">y</span><span class="p">));</span>
<span class="kd">const</span> <span class="nx">funcM</span> <span class="o">=</span> <span class="nx">memoizeN</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">{</span> <span class="na">comparators</span><span class="p">:</span> <span class="nx">sameValueZero</span> <span class="p">});</span> <span class="c1">// equivalen with memoizeN(func)</span>

<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">obj1</span><span class="p">,</span> <span class="nx">obj2</span><span class="p">,</span> <span class="nx">obj3</span><span class="p">));</span> <span class="c1">// =&gt; {c: 3}</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">(</span><span class="nx">obj1</span><span class="p">,</span> <span class="nx">obj2</span><span class="p">,</span> <span class="nx">obj3</span><span class="p">));</span> <span class="c1">// =&gt; {c: 3} // cache hit</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">dir</span><span class="p">(</span><span class="nx">funcM</span><span class="p">.</span><span class="nx">cache</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span> <span class="c1">// =&gt; 1</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>I’ve extracted <code class="language-plaintext highlighter-rouge">memoizeN</code> into <a href="https://gist.github.com/char0n/2e6c77d38cf00faacceaf37e46b76a32" target="_blank" rel="noopener noreferrer">GitHub Gist</a> installable via npm. 
If you prefer to use <code class="language-plaintext highlighter-rouge">memoizeN</code> as npm package,
here is how to achieve that:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"> <span class="nv">$ </span>npm <span class="nb">install </span>gist:2e6c77d38cf00faacceaf37e46b76a32 <span class="nt">--save</span></code></pre></figure>

<p>This command will create the following entry inside your dependencies:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"@char0n/memoizeN"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gist:2e6c77d38cf00faacceaf37e46b76a32"</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>Then use it via CommonJS or ESM:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">memoizeN</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@char0n/memoizeN</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// or</span>
<span class="k">import</span> <span class="nx">memoizeN</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@char0n/memoizeN</span><span class="dl">'</span><span class="p">;</span></code></pre></figure>

<div class="list-group mb-3">
  <a href="https://gist.github.com/char0n/2e6c77d38cf00faacceaf37e46b76a32" class="list-group-item list-group-item-action" target="_blank" rel="noopener noreferrer">
    <div class="d-flex w-100 justify-content-between">
      <h2 class="h5 mb-1"><i class="fa-brands fa-github"></i> memoizeN</h2>
    </div>
    <blockquote class="blockquote fs-6 mb-1">
      Advanced memoization for JavaScript functions with lodash.memoize.
    </blockquote>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "SoftwareSourceCode",
        "author": { "@id": "https://vladimirgorej.com" },
        "name": "memoizeN",
        "abstract": "Advanced memoization for JavaScript functions with lodash.memoize.",
        "codeRepository": "https://gist.github.com/char0n/2e6c77d38cf00faacceaf37e46b76a32"
      }
    </script>
  </a>
</div>

<p>If you need even more advanced functionality like LRU cache, TTL, reference counting, and others,
I suggest you look at <a href="https://github.com/medikoo/memoizee" target="_blank" rel="noopener noreferrer">memoizee</a>. memizee is probably the most
feature-rich implementation of memoization for JavaScript.</p>

<hr />

<div class="alert alert-info" role="alert">
  All code snippets in this article are licensed under <a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="noopener noreferrer">Apache 2.0 license</a> using the following copyright notice: <strong>Copyright 2022 Vladimír Gorej</strong>.
</div>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/lodash.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/lodash.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to handle errors in SwaggerUI</title><link href="https://vladimirgorej.com/blog/swagger-ui-error-handling/" rel="alternate" type="text/html" title="How to handle errors in SwaggerUI" /><published>2022-01-24T08:00:00+00:00</published><updated>2022-01-24T08:00:00+00:00</updated><id>https://vladimirgorej.com/blog/swagger-ui-error-handling</id><content type="html" xml:base="https://vladimirgorej.com/blog/swagger-ui-error-handling/"><![CDATA[<p class="lead">
  Error handling is an essential aspect of today's modern Single Page Applications.
  Error handling refers to the anticipation, detection, and resolution of different kinds of errors.
  As of version <a href="https://www.npmjs.com/package/swagger-ui/v/4.3.0" target="_blank" rel="noopener noreferrer">v4.3.0</a>, <a href="https://github.com/swagger-api/swagger-ui" target="_blank" rel="noopener noreferrer">SwaggerUIs</a> error handling capabilities have considerably improved
  and allowed SwaggerUI integrators to easily integrate their custom errors handlers. 
</p>

<p>Original SwaggerUI error handling consisted of wrapping every render method of every class component
with an imperative <code class="language-plaintext highlighter-rouge">try/catch</code> statement and displaying the <code class="language-plaintext highlighter-rouge">Fallback</code> component if the error was thrown.
Along with displaying the Fallback component, <code class="language-plaintext highlighter-rouge">console.error</code> was used to display the caught error in the browser console.
This solution required wrapping every function component into class component to make sure that
the component has the <code class="language-plaintext highlighter-rouge">render</code> method.</p>

<p>React 16 introduces a new concept of an <a href="https://reactjs.org/docs/error-boundaries.html" target="_blank" rel="noopener noreferrer">error boundary</a>.
It’s no longer necessary to create a custom solution for catching errors in React apps, as we have a standardized way of doing it.</p>

<p>In order to fully utilize new error boundaries, I had to rewrite SwaggerUIs <a href="https://github.com/swagger-api/swagger-ui/tree/master/src/core/plugins/view" target="_blank" rel="noopener noreferrer">view plugin</a>, which all other 
plugins build on. An additional new plugin called <code class="language-plaintext highlighter-rouge">safe-render</code> was introduced to allow configurable
application of error boundaries to SwaggerUI components.</p>

<h2 id="view-plugin">View plugin</h2>

<p>View plugins <strong>public API didn’t change</strong>, I’ve just added one additional util function called <code class="language-plaintext highlighter-rouge">getDisplayName</code>
which is used for accessing React component names.</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="p">{</span>
  <span class="nl">rootInjects</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">getComponent</span><span class="p">:</span> <span class="nx">memGetComponent</span><span class="p">,</span>
    <span class="na">makeMappedContainer</span><span class="p">:</span> <span class="nx">memMakeMappedContainer</span><span class="p">,</span>
    <span class="na">render</span><span class="p">:</span> <span class="nx">render</span><span class="p">(</span><span class="nx">getSystem</span><span class="p">,</span> <span class="nx">getStore</span><span class="p">,</span> <span class="nx">getComponent</span><span class="p">,</span> <span class="nx">getComponents</span><span class="p">),</span>
  <span class="p">},</span>
  <span class="nx">fn</span><span class="p">:</span> <span class="p">{</span>
    <span class="nx">getDisplayName</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">view</code> plugin is no longer responsible for error handling logic. Rewriting the plugin had several
positive side effects like making the SwaggerUI rendering faster and simplifying the React component
tree in React developer tools:</p>

<figure class="figure d-block border">
  <img src="/assets/img/blog/swagger-ui-error-handling-rdtd-before.webp" width="539" height="466" class="figure-img rounded mx-auto d-block img-fluid" alt="todoList plugin" />
  <figcaption class="figure-caption text-center">Before refactor</figcaption>
</figure>

<figure class="figure d-block border">
  <img src="/assets/img/blog/swagger-ui-error-handling-rdtd-after.webp" width="551" height="305" class="figure-img rounded mx-auto d-block img-fluid" alt="todoList plugin" />
  <figcaption class="figure-caption text-center">After refactor</figcaption>
</figure>

<h2 id="safe-render-plugin">Safe-render plugin</h2>

<p>This plugin is solely responsible for error handling logic in SwaggerUI. Accepts a list of component
names that should be protected by error boundaries.</p>

<p>Its public API looks like this:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="p">{</span>
  <span class="nl">fn</span><span class="p">:</span> <span class="p">{</span>
    <span class="nx">componentDidCatch</span><span class="p">,</span>
    <span class="na">withErrorBoundary</span><span class="p">:</span> <span class="nx">withErrorBoundary</span><span class="p">(</span><span class="nx">getSystem</span><span class="p">),</span>
  <span class="p">},</span>
  <span class="nx">components</span><span class="p">:</span> <span class="p">{</span>
    <span class="nx">ErrorBoundary</span><span class="p">,</span>
    <span class="nx">Fallback</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>safe-render plugin is automatically utilized by <a href="https://github.com/swagger-api/swagger-ui/blob/78f62c300a6d137e65fd027d850981b010009970/src/core/presets/base.js" target="_blank" rel="noopener noreferrer">base</a> and <a href="https://github.com/swagger-api/swagger-ui/tree/78f62c300a6d137e65fd027d850981b010009970/src/standalone" target="_blank" rel="noopener noreferrer">standalone</a> SwaggerUI presets and
should always be used as the last plugin, after all the components are already known to the SwaggerUI.
The plugin defines a default list of components that should be protected by error boundaries:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="p">[</span>
  <span class="dl">"</span><span class="s2">App</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">BaseLayout</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">VersionPragmaFilter</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">InfoContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">ServersContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">SchemesContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">AuthorizeBtnContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">FilterContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">Operations</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">OperationContainer</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">parameters</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">responses</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">OperationServers</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">Models</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">ModelWrapper</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">Topbar</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">StandaloneLayout</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">onlineValidatorBadge</span><span class="dl">"</span>
<span class="p">]</span></code></pre></figure>

<p>As demonstrated below, additional components can be protected by utilizing the safe-render plugin 
with configuration options. This gets really handy if you are a SwaggerUI integrator and you maintain a number of
plugins with additional custom components.</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">swaggerUI</span> <span class="o">=</span> <span class="nx">SwaggerUI</span><span class="p">({</span>
  <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://petstore.swagger.io/v2/swagger.json</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">dom_id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#swagger-ui</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">plugins</span><span class="p">:</span> <span class="p">[</span>
    <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">({</span>
      <span class="na">components</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">MyCustomComponent1</span><span class="p">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="dl">'</span><span class="s1">my custom component</span><span class="dl">'</span><span class="p">,</span>
      <span class="p">},</span>
    <span class="p">}),</span>
    <span class="nx">SwaggerUI</span><span class="p">.</span><span class="nx">plugins</span><span class="p">.</span><span class="nx">SafeRender</span><span class="p">({</span>
      <span class="na">fullOverride</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// only the component list defined here will apply (not the default list)</span>
      <span class="na">componentList</span><span class="p">:</span> <span class="p">[</span>
        <span class="dl">"</span><span class="s2">MyCustomComponent1</span><span class="dl">"</span><span class="p">,</span>
      <span class="p">],</span>
    <span class="p">}),</span>
  <span class="p">],</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></figure>

<h3 id="componentdidcatch">componentDidCatch</h3>

<p>This static function is invoked after a component has thrown an error.<br />
It receives two parameters:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">error</code> - The error that was thrown.</li>
  <li><code class="language-plaintext highlighter-rouge">info</code> - An object with a componentStack key containing <a href="https://reactjs.org/docs/error-boundaries.html#component-stack-traces" target="_blank" rel="noopener noreferrer">information about which component threw the error</a>.</li>
</ol>

<p>It has precisely the same signature as error boundaries <a href="https://reactjs.org/docs/react-component.html#componentdidcatch" target="_blank" rel="noopener noreferrer">componentDidCatch lifecycle method</a>,
except it’s a static function and not a class method.</p>

<p>Default implement of componentDidCatch uses <code class="language-plaintext highlighter-rouge">console.error</code> to display the received error:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="k">export</span> <span class="kd">const</span> <span class="nx">componentDidCatch</span> <span class="o">=</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">;</span></code></pre></figure>

<p>To utilize your own error handling logic (e.g. <a href="https://www.bugsnag.com/" target="_blank" rel="noopener noreferrer">bugsnag</a>), create new SwaggerUI plugin that overrides componentDidCatch:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">BugsnagErrorHandlerPlugin</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="c1">// init bugsnag</span>

  <span class="k">return</span> <span class="p">{</span>
    <span class="na">fn</span><span class="p">:</span> <span class="p">{</span>
      <span class="nx">componentDidCatch</span> <span class="o">=</span> <span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">info</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">Bugsnag</span><span class="p">.</span><span class="nx">notify</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
        <span class="nx">Bugsnag</span><span class="p">.</span><span class="nx">notify</span><span class="p">(</span><span class="nx">info</span><span class="p">);</span>
      <span class="p">},</span>
    <span class="p">},</span>
  <span class="p">};</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></figure>

<h3 id="witherrorboundary">withErrorBoundary</h3>

<p>This function is HOC (Higher Order Component). It wraps a particular component into the <code class="language-plaintext highlighter-rouge">ErrorBoundary</code> component.
It can be overridden via a plugin system to control how components are wrapped by the ErrorBoundary component.
In 99.9% of situations, you won’t need to override this function, but if you do, please read the source code of this function first.</p>

<h3 id="fallback">Fallback</h3>

<p>The component is displayed when the error boundary catches an error. It can be overridden via a plugin system.
Its default implementation is trivial:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span>
<span class="k">import</span> <span class="nx">PropTypes</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">prop-types</span><span class="dl">"</span>

<span class="kd">const</span> <span class="nx">Fallback</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">name</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">(</span>
  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">fallback</span><span class="dl">"</span><span class="o">&gt;</span>
    <span class="err">😱</span> <span class="o">&lt;</span><span class="nx">i</span><span class="o">&gt;</span><span class="nx">Could</span> <span class="nx">not</span> <span class="nx">render</span> <span class="p">{</span> <span class="nx">name</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">t</span><span class="dl">"</span> <span class="p">?</span> <span class="dl">"</span><span class="s2">this component</span><span class="dl">"</span> <span class="p">:</span> <span class="nx">name</span> <span class="p">},</span> <span class="nx">see</span> <span class="nx">the</span> <span class="nx">console</span><span class="p">.</span><span class="o">&lt;</span><span class="sr">/i</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span><span class="p">)</span>
<span class="nx">Fallback</span><span class="p">.</span><span class="nx">propTypes</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">name</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span><span class="p">.</span><span class="nx">isRequired</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">export</span> <span class="k">default</span> <span class="nx">Fallback</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Feel free to override it to match your look &amp; feel:</p>

<figure class="highlight"><pre><code class="language-js" data-lang="js"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">CustomFallbackPlugin</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">({</span>
  <span class="na">components</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">Fallback</span><span class="p">:</span> <span class="p">({</span> <span class="nx">name</span> <span class="p">}</span> <span class="p">)</span> <span class="o">=&gt;</span> <span class="s2">`This is my custom fallback. </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2"> failed to render`</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">});</span>

<span class="kd">const</span> <span class="nx">swaggerUI</span> <span class="o">=</span> <span class="nx">SwaggerUI</span><span class="p">({</span>
  <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://petstore.swagger.io/v2/swagger.json</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">dom_id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#swagger-ui</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">plugins</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">CustomFallbackPlugin</span><span class="p">,</span>
  <span class="p">]</span>  
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></figure>

<h3 id="errorboundary">ErrorBoundary</h3>

<p>This is the component that implements React error boundaries. Uses <code class="language-plaintext highlighter-rouge">componentDidCatch</code> and <code class="language-plaintext highlighter-rouge">Fallback</code>
under the hood. In 99.9% of situations, you won’t need to override this component, but if you do, 
please read the source code of this component first.</p>

<h2 id="change-in-behavior">Change in behavior</h2>

<p>In prior releases of SwaggerUI, almost all components have been protected, and when thrown error,
<code class="language-plaintext highlighter-rouge">Fallback</code> component was displayed. This changes with SwaggerUI v4.3.0. Only components defined
by the <code class="language-plaintext highlighter-rouge">safe-render</code> plugin are now protected and display fallback. If a small component somewhere within
SwaggerUI React component tree fails to render and throws an error, the error bubbles up to the closest
error boundary, and that error boundary displays the <code class="language-plaintext highlighter-rouge">Fallback</code> component and invokes <code class="language-plaintext highlighter-rouge">componentDidCatch</code>.</p>

<p>If you’re interested in more technical details, here is the <a href="https://github.com/swagger-api/swagger-ui/pull/7761/files" target="_blank" rel="noopener noreferrer">PR</a>
that introduced the new error handling into SwaggerUI.</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Error handling is an essential aspect of today's modern Single Page Applications. Error handling refers to the anticipation, detection, and resolution of different kinds of errors. As of version v4.3.0, SwaggerUIs error handling capabilities have considerably improved and allowed SwaggerUI integrators to easily integrate their custom errors handlers.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/swagger-ui-error-handling.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/swagger-ui-error-handling.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to migrate from querystring to URLSearchParams in Node.js</title><link href="https://vladimirgorej.com/blog/how-to-migrate-from-querystring-to-url-search-params-in-nodejs/" rel="alternate" type="text/html" title="How to migrate from querystring to URLSearchParams in Node.js" /><published>2021-10-14T07:00:00+00:00</published><updated>2021-10-14T07:00:00+00:00</updated><id>https://vladimirgorej.com/blog/how-to-migrate-from-querystring-to-url-search-params-in-nodejs</id><content type="html" xml:base="https://vladimirgorej.com/blog/how-to-migrate-from-querystring-to-url-search-params-in-nodejs/"><![CDATA[<p class="lead">
  Node.js marked the <a href="https://nodejs.org/docs/latest-v16.x/api/querystring.html" target="_blank" rel="noopener noreferrer">querystring</a> as legacy API in version 14.x,
  and recommends using <a href="https://nodejs.org/docs/latest-v16.x/api/url.html#url_class_urlsearchparams" target="_blank" rel="noopener noreferrer">URLSearchParams</a>. 
  But doesn't give us any clue how to actually migrate. This article fills this void and provides a migration guide.
</p>

<h2 id="querystring">querystring</h2>

<p><em>querystring</em> API is quite simple and straightforward:</p>

<dl class="row">
  <dt class="col-sm-3">querystring.parse()</dt>
  <dd class="col-sm-9">Parses a URL query string into a collection of key and value pairs. <span class="text-muted">querystring.decode()</span> is an alias.</dd>

  <dt class="col-sm-3">querystring.stringify()</dt>
  <dd class="col-sm-9">Produces a URL query string from a given obj by iterating through the object's "own properties". <span class="text-muted">querystring.encode()</span> is an alias.</dd>

  <dt class="col-sm-3">querystring.escape()</dt>
  <dd class="col-sm-9">Performs URL percent-encoding on the given string in a manner that is optimized for the specific requirements of URL query strings. Is used by <span class="text-muted">querystring.stringify()</span>.</dd>

  <dt class="col-sm-3">querystring.unescape()</dt>
  <dd class="col-sm-9">Performs decoding of URL percent-encoded characters on the given string. Is used by <span class="text-muted">querystring.parse().</span></dd>
</dl>

<h3 id="parse-a-url-query-string">Parse a URL query string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="p">{</span> <span class="nx">parse</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">querystring</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">parse</span><span class="p">(</span><span class="dl">'</span><span class="s1">foo=bar&amp;abc=xyz&amp;abc=123</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// returns</span>
<span class="c1">// { </span>
<span class="c1">//   foo: 'bar',</span>
<span class="c1">//   abc: ['xyz', '123']</span>
<span class="c1">// }</span></code></pre></figure>

<h3 id="produce-a-url-query-string">Produce a URL query string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="p">{</span> <span class="nx">stringify</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">querystring</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">stringify</span><span class="p">({</span> <span class="na">foo</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">,</span> <span class="na">baz</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">qux</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">quux</span><span class="dl">'</span><span class="p">],</span> <span class="na">corge</span><span class="p">:</span> <span class="dl">''</span> <span class="p">});</span>

<span class="c1">// returns 'foo=bar&amp;baz=qux&amp;baz=quux&amp;corge='</span></code></pre></figure>

<h3 id="perform-url-percent-encoding-on-the-given-string">Perform URL percent-encoding on the given string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="p">{</span> <span class="nx">escape</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">querystring</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">escape</span><span class="p">(</span><span class="dl">'</span><span class="s1">str1 str2</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// returns 'str1%20str2'</span></code></pre></figure>

<h3 id="perform-decoding-of-url-percent-encoded-characters-on-the-given-string">Perform decoding of URL percent-encoded characters on the given string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="p">{</span> <span class="nx">unescape</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">querystring</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">escape</span><span class="p">(</span><span class="dl">'</span><span class="s1">str1%20str2</span><span class="dl">'</span><span class="p">);</span>

<span class="c1">// returns 'str1 str2'</span></code></pre></figure>

<h2 id="urlsearchparams">URLSearchParams</h2>

<p>What’s the actual difference between <code class="language-plaintext highlighter-rouge">querystring</code> vs. <code class="language-plaintext highlighter-rouge">URLSearchParams</code>?</p>

<blockquote class="blockquote">
  <p>"The WHATWG <em>URLSearchParams</em> interface and the <em>querystring</em> module have similar purpose, but the purpose of the <em>querystring</em> module is more general, as it allows the customization of delimiter characters."</p>
  <footer class="blockquote-footer"><cite title="Node.js 14.x documentation">Node.js 14.x documentation</cite></footer>
</blockquote>

<p>Working with URIs/URLs in JavaScript was always confusing. <a href="https://url.spec.whatwg.org/" target="_blank" rel="noopener noreferrer">WHATWG URL specification</a> finally
standardizes the term URL and provides a standardized way how we work with URLs. <em>URLSearchParams</em> is part of this specification.
Now let’s migrate our previous <em>querystring</em> examples to <em>URLSearchParams</em> API.</p>

<h3 id="parse-a-url-query-string-1">Parse a URL query string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="dl">'</span><span class="s1">foo=bar&amp;abc=xyz&amp;abc=123</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// URLSearchParams { 'foo' =&gt; 'bar', 'abc' =&gt; 'xyz', 'abc' =&gt; '123' }</span>

<span class="nx">params</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// 'bar'</span>
<span class="nx">params</span><span class="p">.</span><span class="nx">getAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">abc</span><span class="dl">'</span><span class="p">)</span> <span class="c1">// ['xyz', '123']</span></code></pre></figure>

<h3 id="produce-a-url-query-string-1">Produce a URL query string</h3>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span> <span class="na">foo</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">,</span> <span class="na">baz</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">qux</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">quux</span><span class="dl">'</span><span class="p">],</span> <span class="na">corge</span><span class="p">:</span> <span class="dl">''</span> <span class="p">}).</span><span class="nx">toString</span><span class="p">();</span>
<span class="c1">// returns 'foo=bar&amp;baz=qux%2Cquux&amp;corge='</span></code></pre></figure>

<p>As we can see, what we got is a different result compared to <em>querystring</em>.</p>

<div class="table-responsive">
  <table class="table">
    <thead>
      <tr>
        <th scope="col">API</th>
        <th scope="col">Result</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>querystring</td>
        <td>foo=bar&amp;baz=qux&amp;baz=quux&amp;corge=</td>
      </tr>
      <tr>
        <td>URLSearchParams</td>
        <td>foo=bar&amp;baz=qux%2Cquux&amp;corge=</td>
      </tr>
    </tbody>
  </table>
</div>

<blockquote class="blockquote">
  <p>"Unlike <em>querystring</em> module, duplicate keys in the form of array values are not allowed. Arrays are stringified using array.toString(), which simply joins all array elements with commas."</p>
  <footer class="blockquote-footer"><cite title="Node.js 14.x documentation">Node.js 14.x documentation</cite></footer>
</blockquote>

<p>To get the result compatible with <em>querystring</em>, we have to initialize <em>URLSearchParams</em> with a list of tuples 
instead of object:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">([</span>
  <span class="p">[</span><span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">],</span>
  <span class="p">[</span><span class="dl">'</span><span class="s1">baz</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">qux</span><span class="dl">'</span><span class="p">],</span>
  <span class="p">[</span><span class="dl">'</span><span class="s1">baz</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">quux</span><span class="dl">'</span><span class="p">],</span>
  <span class="p">[</span><span class="dl">'</span><span class="s1">corge</span><span class="dl">'</span><span class="p">,</span> <span class="dl">''</span><span class="p">],</span>
<span class="p">]).</span><span class="nx">toString</span><span class="p">();</span>
<span class="c1">// returns 'foo=bar&amp;baz=qux&amp;baz=quux&amp;corge='</span></code></pre></figure>

<h3 id="perform-url-percent-encoding-on-the-given-string-1">Perform URL percent-encoding on the given string</h3>

<p>There is no low-level API for encoding simple strings in <em>URLSearchParams</em>.
We have to be a little creative to achieve URL encoding. Below
are 2 different yet functionally equivalent ways how to achieve the same result.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">([[</span><span class="dl">''</span><span class="p">,</span> <span class="dl">'</span><span class="s1">str1 str2</span><span class="dl">'</span><span class="p">]]).</span><span class="nx">toString</span><span class="p">().</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span><span class="dl">''</span><span class="p">:</span> <span class="dl">'</span><span class="s1">str1 str2</span><span class="dl">'</span><span class="p">}).</span><span class="nx">toString</span><span class="p">().</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="c1">// returns 'str1+str2'</span></code></pre></figure>

<p>Again, we can see what we got is a different result compared to <em>querystring</em>. WHATWG URL specification
is now precise about how various segments of URL gets encoded. Spaces in query parameters are now plus-sign (<code class="language-plaintext highlighter-rouge">+</code>) encoded, 
compared to percent-encoded spaces in the path segment of the URL. This currently accepted behavior is <strong>backward incompatible</strong> with <em>querystring</em>,
and there is nothing you can do about it except to take it.</p>

<table class="table">
  <thead>
    <tr>
      <th scope="col">API</th>
      <th scope="col">Result</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>querystring</td>
      <td>str1%20str2</td>
    </tr>
    <tr>
      <td>URLSearchParams</td>
      <td>str1+str2</td>
    </tr>
  </tbody>
</table>

<h3 id="perform-decoding-of-url-percent-encoded-characters-on-the-given-string-1">Perform decoding of URL percent-encoded characters on the given string</h3>

<p>There is no low-level API for decoding a simple string in <em>URLSearchParams</em>.
Again we have to be creative to achieve URL decoding on the given string.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">urlPlusEncodedString</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">str1+str2</span><span class="dl">'</span><span class="p">;</span>

<span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="s2">`=</span><span class="p">${</span><span class="nx">urlPlusEncodedString</span><span class="p">}</span><span class="s2">`</span><span class="p">).</span><span class="kd">get</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
<span class="c1">// returns 'str1 str2'</span></code></pre></figure>

<p>URLSearchParams will handle percent-encoded spaces as well:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">urlPercentEncodedString</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">str1%20str2</span><span class="dl">'</span><span class="p">;</span>

<span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="s2">`=</span><span class="p">${</span><span class="nx">urlPercentEncodedString</span><span class="p">}</span><span class="s2">`</span><span class="p">).</span><span class="kd">get</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
<span class="c1">// returns 'str1 str2'</span></code></pre></figure>

<h2 id="other-differences">Other differences</h2>

<p><strong>Handling of question mark character</strong></p>

<p><em>URLSearchParams</em> removes question mark character from the start of the URL query string. <em>querystring</em>
keeps it and makes it part of the key name.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="p">{</span> <span class="nx">parse</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">querystring</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">parse</span><span class="p">(</span><span class="dl">'</span><span class="s1">?foo=bar</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// returns { '?foo': 'bar' }</span>
<span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="dl">'</span><span class="s1">?foo=bar</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// returns URLSearchParams { 'foo' =&gt; 'bar' }</span></code></pre></figure>

<p><strong>Converting URLSearchParams object to Plain JavaScript Object</strong></p>

<p><em>URLSearchParams</em> object is a class instance. It is not a classical Plain JavaScript Object (POJO).
To transform URLSearchParam instance into a POJO, use the following recipe.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">(</span><span class="dl">'</span><span class="s1">foo=bar</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">paramsPOJO</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">fromEntries</span><span class="p">(</span><span class="nx">params</span><span class="p">.</span><span class="nx">entries</span><span class="p">());</span> <span class="c1">// { 'foo': 'bar' }</span></code></pre></figure>

<p><em>Have you found other differences? I’d be happy to add them to this list.</em></p>

<h2 id="closing-words">Closing words</h2>

<p><em>querystring</em> is a legacy Node.js API that shouldn’t be used anymore. New code should only
use <em>URLSearchParams</em>, which is part of <strong>WHATWG URL specification</strong>. Old code using <em>querystring</em>
can easily be migrated to <em>URLSearchParams</em> using this guide. The only <strong>backward incompatibility</strong>
between the two APIs is that <em>URLSearchParams</em> use plus-encoded spaces instead of percent-encoded
spaces in <em>querystring</em>.</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Node.js marked the querystring as legacy API and recommends using URLSearchParams. But doesn't give us any clue how to actually migrate. This article fills this void and provides a migration guide.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/querystring-migration.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/querystring-migration.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to apply the Apache 2.0 License to your Open Source software project</title><link href="https://vladimirgorej.com/blog/how-to-apply-apache2-license-to-your-open-source-software-project/" rel="alternate" type="text/html" title="How to apply the Apache 2.0 License to your Open Source software project" /><published>2021-10-02T12:15:00+00:00</published><updated>2021-10-02T12:15:00+00:00</updated><id>https://vladimirgorej.com/blog/how-to-apply-apache2-license-to-your-open-source-software-project</id><content type="html" xml:base="https://vladimirgorej.com/blog/how-to-apply-apache2-license-to-your-open-source-software-project/"><![CDATA[<p class="lead">
Apache 2.0 License is one of the best OSS licenses to use in your Open Source software project, but do you actually know how to properly apply it? We'll go through all the common mistakes and show how to use the license correctly.
</p>

<p>I’ve always been a <span class="fst-italic">3-Clause BSD License</span> (AKA <i>New BSD License</i> or <span class="fst-italic">Modified BSD License</span>) guy.
As I’ve matured, my understanding of Open Source licensing has grown as well, and so have my requirements. When I start a new Open Source
project now, I just use <strong>Apache 2.0 license</strong>. I want to point out that I am not a lawyer, and I am certainly not your lawyer.
My legal opinions are based on research and lengthy conversations with legal departments of companies that I worked for.</p>

<h2 id="why-apache-20-license">Why Apache 2.0 license?</h2>

<p>There are various reasons why I prefer Apache 2.0 license over the others (specifically the 3-Clause BSD License).
This <a href="https://www.whitesourcesoftware.com/resources/blog/top-10-apache-license-questions-answered/#7_What_is_the_difference_between_Apache_License_20_and_BSD" target="_blank" rel="noopener noreferrer">WhiteSource article</a>
covers the differences nicely, but for completeness, let me mentioned them here explicitly as well:</p>

<dl class="row">
  <dt class="col-sm-3">Explicit grant of patent rights</dt>
  <dd class="col-sm-9">Apache License 2.0 explicitly lays down the grant of patent rights while using, modifying, or distributing Apache licensed software; it also lists the circumstances when such grant gets withdrawn.</dd>

  <dt class="col-sm-3">Precise definitions of the used concepts</dt>
  <dd class="col-sm-9">Apache License 2.0 explicitly defines all the terms and concepts that it uses. This leaves little scope for ambiguity.</dd> 

  <dt class="col-sm-3">Reusable without rewording</dt>
  <dd class="col-sm-9">
    Apache License 2.0 can be easily used by other projects without any rewording in the license document itself.
    It also means that it should be sufficient to provide the canonical URL <code>https://www.apache.org/licenses/LICENSE-2.0.txt</code>, where
    the copy of the license can be obtained, instead of the license text.
  </dd>
</dl>

<p>These differences are most important to me, but other differences like <em>“prominent notices stating that You changed the files”</em>
might be more critical for you. Here is the full text of the Apache 2.0 license:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
</pre></td><td class="code"><pre>                                Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
   Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
   stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
   that You distribute, all copyright, patent, trademark, and
   attribution notices from the Source form of the Work,
   excluding those notices that do not pertain to any part of
   the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
   distribution, then any Derivative Works that You distribute must
   include a readable copy of the attribution notices contained
   within such NOTICE file, excluding those notices that do not
   pertain to any part of the Derivative Works, in at least one
   of the following places: within a NOTICE text file distributed
   as part of the Derivative Works; within the Source form or
   documentation, if provided along with the Derivative Works; or,
   within a display generated by the Derivative Works, if and
   wherever such third-party notices normally appear. The contents
   of the NOTICE file are for informational purposes only and
   do not modify the License. You may add Your own attribution
   notices within Derivative Works that You distribute, alongside
   or as an addendum to the NOTICE text from the Work, provided
   that such additional attribution notices cannot be construed
   as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</pre></td></tr></tbody></table></code></pre></figure>

<p>There are two variants of the Apache 2.0 license: justified version and version with text aligned to the left.
The justified version is the one you get by fetching <code class="language-plaintext highlighter-rouge">https://www.apache.org/licenses/LICENSE-2.0.txt</code> - I call it the <strong>canonical form</strong>.
The license has 201 lines of text and is composed of the header (lines 1-3),
the terms and conditions (lines 5-176) and the appendix (lines 178-201).</p>

<h2 id="license">License</h2>

<p>To apply the license to your software project, create a <code class="language-plaintext highlighter-rouge">LICENSE</code> file in the source tree’s top level.
Now copy the full Apache 2.0 License text from <a href="https://www.apache.org/licenses/LICENSE-2.0.txt" target="_blank" rel="noopener noreferrer">https://www.apache.org/licenses/LICENSE-2.0.txt</a> into a <code class="language-plaintext highlighter-rouge">LICENSE</code> file.
Notice that the <code class="language-plaintext highlighter-rouge">LICENSE</code> file doesn’t have any extension, but you can optionally name the file <code class="language-plaintext highlighter-rouge">LICENSE.txt</code> as well.</p>

<p><strong>Do not modify</strong> the license text. If you used a 3-Clause BSD License in the past,
you usually downloaded the license template, changed it, and saved it as a <code class="language-plaintext highlighter-rouge">LICENSE</code> file.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">Copyright &lt;YEAR&gt; &lt;COPYRIGHT HOLDER&gt;

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.</code></pre></figure>

<p>You may be tempted to do the same with Apache 2.0 License. The license text contains what looks like
a copyright notice on line 189:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">Copyright [yyyy] [name of copyright owner]</code></pre></figure>

<p>Actually, this is a copyright notice template and an additional boilerplate that are part of the Apache 2.0 License appendix, which starts
at line 178 and explains how to apply the Apache License to individual source files instead
of having one single <code class="language-plaintext highlighter-rouge">LICENSE</code> file in the top level of the source tree. The text in the appendix is
very explicit about what the copyright notice template and additional boilerplate are for (use in source files). Changing
this copyright notice template directly in license text is IMHO an improper application of the license.
The whole appendix (lines 178-201) can technically be omitted from the license, as it’s outside the scope of “Section
1 through 9”, which forms the terms and conditions of the license.</p>

<p>My opinion is backed by the “<strong>Reusable without rewording</strong>” attribute of the Apache 2.0 License
I mentioned before. It’s even backed by my experience in a big corporation - two years ago, I was working for one of the biggest tech companies in the world.
The company had established an Open Source office with a Legal Department dealing with use of Open Source within the company.
This Open Source office stipulated that for an Open Source project to be licensed under Apache 2.0 License, it is not required
to have an explicit Apache 2.0 License text as part of the project. 
It is absolutely sufficient if the Open Source project README file
contains the name of the license, either “Apache 2.0 License” or its <a href="https://spdx.org/licenses/Apache-2.0.html" target="_blank" rel="noopener noreferrer">SPDX identifier</a>.
Ideally, (but not required) canonical URL where the copy of the license can be obtained should be mentioned as well: <code class="language-plaintext highlighter-rouge">https://www.apache.org/licenses/LICENSE-2.0.txt</code>.
This is just a legal opinion of one big tech company. Don’t take this legal opinion as implied truth.</p>

<h2 id="copyright-notice">Copyright notice</h2>

<p>In the previous section, we’ve stipulated that the Apache 2.0 License text doesn’t actually have a place where to
put an explicit copyright notice. Since 1989, <a href="https://www.wipo.int/treaties/en/ip/berne/" target="_blank" rel="noopener noreferrer">Berne Convention</a> established that the copyright is automatic,
and the use of copyright notices to claim the copyright has become optional.</p>

<p>This is how an <strong>explicit copyright notice</strong> looks like:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">Copyright 2021 Vladimír Gorej &lt;vladimir.gorej@gmail.com&gt;</code></pre></figure>

<p>When an explicit copyright notice is missing in Open Source software that you want to use in your project,
you’re theoretically still OK, as it is possible to compile one using the following assumptions:</p>

<ol>
  <li>The name of the copyright holder is the name of the first committer to the project</li>
  <li>The copyright holder contact is established by using the email address of the first committer</li>
  <li>The year of the copyright notice is established as the year of the first commit to the project</li>
</ol>

<p>If you cannot somehow obtain the above information, you’re kind of screwed, as there is no way to 
establish the copyright notice for compliance purposes in big tech companies.</p>

<p>It’s always a good idea to provide an explicit copyright notice when applying the Apache 2.0 License. But, where do we put one?
Well, if we would read the Apache 2.0 Licence terms and conditions carefully, <strong>section 4.d)</strong> (line 106) has an answer for that: <strong>a “NOTICE” text file</strong>.
Just create a new file called <code class="language-plaintext highlighter-rouge">NOTICE</code> and put your copyright notice there.</p>

<p>Today there are three competing formats of copyright notices:</p>

<dl class="row">
  <dt class="col-sm-3">Historical format</dt>
  <dd class="col-sm-9">
    Copyright 2017-2019, Vladimír Gorej<br />
    Copyright 1998, Linus Torvalds
  </dd>

  <dt class="col-sm-3">Newer format</dt>
  <dd class="col-sm-9">
    Copyright The Ramda Adjunct contributors<br />
    Copyright The Kubernetes Authors
  </dd>

  <dt class="col-sm-3">Hybrid format</dt>
  <dd class="col-sm-9">
    Copyright 2017-2019 Vladimír Gorej and the Ramda Adjunct contributors
  </dd>
</dl>

<p>Given above, your <code class="language-plaintext highlighter-rouge">NOTICE</code> file could look like this:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">Ramda Adjunct
Copyright 2017, Vladimír Gorej</code></pre></figure>

<h2 id="closing-words">Closing words</h2>

<p>Choose the appropriate Open Source license for the needs of your project.
If you’re deciding between BSD, MIT, and Apache 2.0, go for Apache 2.0 License. The license
automatically grants patent rights. It’s clear, explicit, and reusable without rewording. 
To apply the license to your Open Source software project, create two files: <code class="language-plaintext highlighter-rouge">LICENSE</code> and <code class="language-plaintext highlighter-rouge">NOTICE</code>
in the top level of the source tree. The copy of the license text that you obtained from <code class="language-plaintext highlighter-rouge">https://www.apache.org/licenses/LICENSE-2.0.txt</code>
goes into the <code class="language-plaintext highlighter-rouge">LICENSE</code> file. Your copyright notice goes into the <code class="language-plaintext highlighter-rouge">NOTICE</code> file. Doing these
two things makes the application of the Apache 2.0 License as explicit and clear as it can be.
Sounds pretty easy, right?</p>

<p>Another good source of information about applying Apache 2.0 License to a software project
can be official <a href="https://infra.apache.org/licensing-howto.html" target="_blank" rel="noopener noreferrer">“How to”</a> from Apache Software Foundation
directed to Apache Committers.</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Apache 2.0 License is one of the best OSS licenses to use in your Open Source software project, but do you actually know how to properly apply it? We'll go through all the common mistakes and show how to use the license correctly.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/apache-logo.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/apache-logo.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Things I have learned while maintaining JavaScript monorepo with Lerna</title><link href="https://vladimirgorej.com/blog/things-i-have-learned-maintaining-javascript-monorepo-with-lerna/" rel="alternate" type="text/html" title="Things I have learned while maintaining JavaScript monorepo with Lerna" /><published>2021-09-07T12:15:00+00:00</published><updated>2021-09-07T12:15:00+00:00</updated><id>https://vladimirgorej.com/blog/things-i-have-learned-maintaining-javascript-monorepo-with-lerna</id><content type="html" xml:base="https://vladimirgorej.com/blog/things-i-have-learned-maintaining-javascript-monorepo-with-lerna/"><![CDATA[<p class="lead">
  This is the second part of the article series dealing with <i>monorepo</i> management using <a href="https://github.com/lerna/lerna" target="_blank" rel="noopener noreferrer">Lerna</a>.
  <a href="https://www.linkedin.com/pulse/things-i-wish-had-known-when-started-javascript-monorepo-gorej/" target="_blank" rel="noopener noreferrer">The first part</a> dealt
  with <i>monorepo</i> management in depth using Lerna and <a href="https://docs.npmjs.com/cli/v7/configuring-npm/pacage-json#local-paths" target="_blank" rel="noopener noreferrer">npm local paths</a>
  long before npm@7 workspaces were introduced. This article will try to describe <i>monorepo</i> management practices
  in the world of <a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces" target="_blank" rel="noopener noreferrer">npm@7</a> workspaces. Nevertheless, I highly recommend you read the first part
  of this series to be familiar with how things used to be.
</p>

<h2 id="what-is-monorepo">What is monorepo?</h2>

<p>Let’s first define what <em>monorepo</em> is:</p>

<blockquote class="blockquote">
  <p>Monorepo is a software development strategy where code for many projects is stored in the same repository.</p>
</blockquote>

<p><em>Monorepo</em> code is usually divided into separate contexts, called packages. Every package contains code specific to its context and can depend on zero or more other packages within monorepo. Monorepo must have a mechanism to interconnect packages and allow us to use one package code within another.</p>

<p>In the following texts, I’m assuming you have your <em>monorepo</em> setup as described in the first part of this series.</p>

<h2 id="dependency-management">Dependency management</h2>

<p>Dependency managed has been simplified significantly with npm workspaces. npm workspaces require npm@7.
This version of npm uses the <a href="https://github.blog/2021-02-02-npm-7-is-now-generally-available/#changes-to-the-lockfile" target="_blank" rel="noopener noreferrer">new format</a> of a <code class="language-plaintext highlighter-rouge">package-lock.json</code> file.
You’ll need to convert your <code class="language-plaintext highlighter-rouge">package-lock.json</code> file to the new format for workspaces to work correctly.</p>

<h3 id="a-word-on-expressing-dependencies-between-packages">A word on expressing dependencies between packages</h3>

<p>We now use <strong>asterisk</strong> symbol to express dependencies between different <em>monorepo</em> packages.
Given that following are dependencies of <code class="language-plaintext highlighter-rouge">package-c</code> which, requires <code class="language-plaintext highlighter-rouge">package-a</code> and <code class="language-plaintext highlighter-rouge">package-b</code>,
this is how we express it in <code class="language-plaintext highlighter-rouge">package.json</code>:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"ramda"</span><span class="p">:</span><span class="w"> </span><span class="s2">"=0.27.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"ramda-adjunct"</span><span class="p">:</span><span class="w"> </span><span class="s2">"=2.27.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"package-a"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"package-b"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Notice that we no longer use explicit npm local paths but rather an <strong>asterisk</strong> symbol.
<strong>Asterisk</strong> symbol is translated to npm local paths under the hood in a <code class="language-plaintext highlighter-rouge">package-lock.json</code> file.</p>

<p>Before npm workspaces, we had to list all packages in the <code class="language-plaintext highlighter-rouge">dependencies</code> field of the main <em>monorepo</em> <code class="language-plaintext highlighter-rouge">package.json</code> file.</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"package-a"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:packages/package-a"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"package-b"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:packages/package-b"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"package-c"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:packages/package-c"</span><span class="w">
</span><span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>This is no longer required. npm workspaces support recognizing <em>monorepo</em> packages by defining
a <code class="language-plaintext highlighter-rouge">workspaces</code> field in the main <em>monorepo</em> <code class="language-plaintext highlighter-rouge">package.json</code> file:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="nl">"workspaces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  </span><span class="s2">"./packages/*"</span><span class="w">
</span><span class="p">]</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>We don’t need to add a new package explicitly in the <code class="language-plaintext highlighter-rouge">dependency</code> field whenever we want to add one. This saves
time and prevents cryptic errors when we forget to add the package explicitly as a dependency.</p>

<h3 id="a-word-on-development-dependencies">A word on development dependencies</h3>

<p>It is no longer required to keep all development dependencies in main the <em>monorepo</em> <code class="language-plaintext highlighter-rouge">package.json</code> file.
Every <em>monorepo</em> package can now have its own development dependencies in a particular version, given
the specific needs.</p>

<p>Before npm workspaces, the <code class="language-plaintext highlighter-rouge">devDependencies</code> field of every package <code class="language-plaintext highlighter-rouge">package.json</code> file was ignored.
Now, npm@7 recognizes it and installs the npm packages listed in <code class="language-plaintext highlighter-rouge">devDependencies</code>.</p>

<p>IMHO it still makes sense to keep certain <code class="language-plaintext highlighter-rouge">devDepedendies</code> (like testing frameworks) in the main
<em>monorepo</em> <code class="language-plaintext highlighter-rouge">package.json</code> file.</p>

<h3 id="a-word-on-topology">A word on topology</h3>

<p>npm workspaces are aware of <em>monorepo</em> package topology. Workspaces know that
<code class="language-plaintext highlighter-rouge">package-c</code> uses <code class="language-plaintext highlighter-rouge">package-a</code> and <code class="language-plaintext highlighter-rouge">package-b</code> as its dependencies. Let’s run the following command, which runs build npm script
in all the <em>monorepo</em> packages:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"> <span class="nv">$ </span>npm run build <span class="nt">--workspaces</span></code></pre></figure>

<p>This command will run <code class="language-plaintext highlighter-rouge">npm run build</code> for all the <em>monorepo</em> packages.
But it seems that npm workspaces don’t run the monorepo packages command in order as expected.
Let’s say that <code class="language-plaintext highlighter-rouge">package-a</code> depends on <code class="language-plaintext highlighter-rouge">package-b</code> and <code class="language-plaintext highlighter-rouge">package-c</code> depends both on <code class="language-plaintext highlighter-rouge">package-a</code> and <code class="language-plaintext highlighter-rouge">package-b</code>.
The order of execution you get from running the command is:</p>

<ul>
  <li>package-a</li>
  <li>package-b</li>
  <li>package-c</li>
</ul>

<p>But the correct order of build should be:</p>

<ul>
  <li>package-b</li>
  <li>package-a</li>
  <li>package-c</li>
</ul>

<p>Proper order of execution can be achieved by defining each package explicitly and in the correct order 
in the <code class="language-plaintext highlighter-rouge">workspaces</code> array of <code class="language-plaintext highlighter-rouge">package.json</code>. This array then needs to be maintained manually to reflect the monorepo topology.</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"workspaces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"./packages/package-b"</span><span class="p">,</span><span class="w"> </span><span class="s2">"./package/package-a"</span><span class="p">,</span><span class="w"> </span><span class="s2">"./packages/package-c"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>Because of this, <strong>Lerna</strong> still plays a significant role here.
Lerna will determine which packages have <strong>build</strong> npm script defined, and then it determines the proper execution order.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"> <span class="nv">$ </span>lerna run build</code></pre></figure>

<p>Order of execution:</p>

<ul>
  <li>package-b</li>
  <li>package-a</li>
  <li>package-c</li>
</ul>

<h3 id="a-word-on-installing-a-new-dependency">A word on installing a new dependency</h3>

<p>npm workspaces completely simplified the process of adding a new production dependency. Let’s say
we have a <code class="language-plaintext highlighter-rouge">package-a</code>, and we want to add a dependency of <code class="language-plaintext highlighter-rouge">ramda-adjunct@2.27.0</code>. The only thing 
we need to do is run the following command:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  <span class="nv">$ </span>npm <span class="nb">install </span>ramda-adjunct@2.27.0 <span class="nt">--workspace</span><span class="o">=</span>package-a <span class="nt">--save</span></code></pre></figure>

<h3 id="a-word-on-installing-a-new-development-dependency">A word on installing a new development dependency</h3>

<p>As already stipulated, development dependencies can be installed either in top-level <em>monorepo</em> (suitable for testing frameworks)</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"> <span class="nv">$ </span>npm <span class="nb">install </span>mocha <span class="nt">--save-dev</span></code></pre></figure>

<p>Or installed as a specific development dependency of a particular <em>monorepo</em> package</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"> <span class="nv">$ </span>npm <span class="nb">install </span>rimraf <span class="nt">--workspace</span><span class="o">=</span>package-a <span class="nt">--save-dev</span></code></pre></figure>

<h3 id="a-word-on-npm-binaries">A word on npm binaries</h3>

<p>Before npm workspaces, to run a binary from one of the <em>monorepo</em> packages, we had to use the <a href="https://www.npmjs.com/package/link-parent-bin" target="_blank" rel="noopener noreferrer">link-parent-bin</a>
npm package to link top-level npm binaries to all <em>monorepo</em> packages. This is no longer required as npm supports this natively.</p>

<h2 id="closing-words">Closing words</h2>

<p>Given all that has been written here, I think npm workspaces still doesn’t replace Lerna. Instead,
they just significantly simplified how we work with Lerna. Lerna still plays a significant part in
change detection, releases, and running npm scripts in the proper order (given the package topology).</p>

<p>All the examples mentioned in the article can be found in the following Lerna monorepo repository I’ve setup-ed for you. Clone it, install it, play with it and have a lot of fun with it!</p>

<div class="list-group mb-3">
  <a href="https://github.com/char0n/lerna-monorepo-taming" class="list-group-item list-group-item-action" target="_blank" rel="noopener noreferrer">
    <div class="d-flex w-100 justify-content-between">
      <h2 class="h5 mb-1"><i class="fa-brands fa-github"></i> lerna-monorepo-taming</h2>
    </div>
    <blockquote class="blockquote fs-6 mb-1">
      This monorepo implements current practices and conventions used while building monorepos using Lerna.
    </blockquote>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "SoftwareSourceCode",
        "author": { "@id": "https://vladimirgorej.com" },
        "name": "lerna-monorepo-taming",
        "abstract": "This monorepo implements current practices and conventions used while building monorepos using Lerna.",
        "codeRepository": "https://github.com/char0n/lerna-monorepo-taming"
      }
    </script>
  </a>
</div>

<hr />

<p><strong>Maintaining Lerna monorepo series:</strong></p>

<ul>
  <li>Part 1: <a href="https://www.linkedin.com/pulse/things-i-wish-had-known-when-started-javascript-monorepo-gorej/" target="_blank" rel="noopener noreferrer">Things I wish I had known when I started JavaScript monorepo with Lerna</a></li>
  <li>Part 2: Things I have learned while maintaining JavaScript monorepo with Lerna</li>
</ul>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Monorepo management processes using Lerna and npm@7 workspaces]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/taming-lerna.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/taming-lerna.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to validate OpenAPI definitions in Swagger Editor using GitHub Actions</title><link href="https://vladimirgorej.com/blog/how-to-validate-openapi-definitions-in-swagger-editor-using-github-actions/" rel="alternate" type="text/html" title="How to validate OpenAPI definitions in Swagger Editor using GitHub Actions" /><published>2021-03-07T08:39:04+00:00</published><updated>2021-03-07T08:39:04+00:00</updated><id>https://vladimirgorej.com/blog/how-to-validate-openapi-definitions-in-swagger-editor-using-github-actions</id><content type="html" xml:base="https://vladimirgorej.com/blog/how-to-validate-openapi-definitions-in-swagger-editor-using-github-actions/"><![CDATA[<p class="lead">
  Building a GitHub Action workflow that uses Swagger Editor to validate an OpenAPI definition.
</p>

<p>The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to HTTP APIs,
allowing both humans and computers to discover and understand the service’s capabilities without
access to source code, documentation, or network traffic inspection.
When properly defined, a consumer can understand and interact with the remote service with minimal implementation logic.</p>

<p>An OpenAPI definition can then be used by documentation generation tools to display the API, code generation
tools to generate servers and clients in various programming languages, testing tools, and many other use cases.</p>

<p>I took the above description of what OpenAPI is directly from <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md" target="_blank" rel="noopener noreferrer">its specification</a>.
If you’re one of the people who write OpenAPI definition by hand in <a href="https://editor.swagger.io/" target="_blank" rel="noopener noreferrer">Swagger Editor</a>, this article was written just for you.</p>

<p>A couple of years ago, I worked with <a href="https://www.ui.com/" target="_blank" rel="noopener noreferrer">Ubiquiti Inc</a>. We were producing great wireless hardware solutions
with embedded UIs. I was fortunate to have joined the company when a cross hardware configuration system
called <a href="https://unms.com/" target="_blank" rel="noopener noreferrer">UNMS</a> was being architected and developed. The backend part of the system consisted of a vast <a href="https://en.wikipedia.org/wiki/Representational_state_transfer" target="_blank" rel="noopener noreferrer">REST API</a> layer that 
the Frontend layer was consuming. We used a design-first approach to producing the API. The workflow of this 
<a href="https://www.visual-paradigm.com/guide/development/code-first-vs-design-first" target="_blank" rel="noopener noreferrer">design-first approach</a> consisted of the following steps:</p>

<ul>
  <li>create changes in <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md" target="_blank" rel="noopener noreferrer">OpenApi 2.0</a> definition using Swagger Editor</li>
  <li>issuer: issue a <a href="https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests" target="_blank" rel="noopener noreferrer">GitHub Pull Request</a> for the team to review how new API endpoints could look like</li>
  <li>reviewer: paste the OpenAPI definition from Pull Request to Swagger Editor to see if no errors were introduced</li>
  <li>merge Pull Request</li>
  <li>implement the actual REST API defined in OpenAPI definition</li>
</ul>

<p>Today I’d immediately think about how to automate this workflow and make <a href="https://en.wikipedia.org/wiki/Continuous_integration" target="_blank" rel="noopener noreferrer">Continuous Integration</a> do the work for us.
Unfortunately, at that time, my knowledge about CI/CD pipelines was limited, and I wasn’t aware of the full benefits 
and values CI/CD provides.</p>

<p>Some time has passed since then. I did my homework and studied CI/CD topic thoroughly. When <a href="https://github.com/features/actions" target="_blank" rel="noopener noreferrer">GitHub Actions</a> were born
I immediately became a massive fan. Let’s try to automate the workflow described above using GitHub Actions.</p>

<h2 id="technical-design">Technical design</h2>

<p>We need to produce a GitHub Action that uses Swagger Editor to validate the OpenAPI definition provided
as a parameter to that action. Using a Swagger Editor in GitHub Action can be achieved in two ways:
running it in a docker container using <a href="https://hub.docker.com/r/swaggerapi/swagger-editor/" target="_blank" rel="noopener noreferrer">swagger-api/swagger-editor image</a> or using <a href="https://editor.swagger.io/" target="_blank" rel="noopener noreferrer">https://editor.swagger.io/</a> directly.
Now that we have Swagger Editor running, we use <a href="https://pptr.dev/" target="_blank" rel="noopener noreferrer">puppeteer</a> to open a headless version of Chromium Browser.
Then we paste the OpenAPI definition into the Swagger Editor and wait for it to render errors
(if the definition is valid, no errors will be generated). Read errors from the Swagger Editor using puppeteer 
and represent them via GitHub Actions API. GitHub Action will report failure on any error or success if
the document is valid and contains no errors.</p>

<p>Why use Swagger Editor and not just JSON Schema validation? Swagger Editor adds its own layer of error recognition on top of JSON Schema validation.
Unfortunately, this additional layer is tightly coupled to Swagger Editor code, and the easiest way to use it is to use it via running
the Swagger Editor in a browser.</p>

<h2 id="implementation">Implementation</h2>

<p>The implementation wasn’t that straightforward as I expected, but I still managed to implement this GitHub Action
against the technical design mentioned earlier with all its requirements. I called it <a href="https://github.com/char0n/swagger-editor-validate" target="_blank" rel="noopener noreferrer">swagger-editor-validate</a>.</p>

<div class="list-group mb-3">
  <a href="https://github.com/char0n/swagger-editor-validate" class="list-group-item list-group-item-action" target="_blank" rel="noopener noreferrer">
    <div class="d-flex w-100 justify-content-between">
      <h2 class="h5 mb-1"><i class="fa-brands fa-github"></i> swagger-editor-validate</h2>
    </div>
    <blockquote class="blockquote fs-6 mb-1">
      This GitHub Actions validates OpenAPI (OAS) definition file using Swagger Editor.
    </blockquote>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "SoftwareSourceCode",
        "author": { "@id": "https://vladimirgorej.com" },
        "name": "swagger-editor-validate",
        "abstract": "This GitHub Actions validates OpenAPI (OAS) definition file using Swagger Editor.",
        "codeRepository": "https://github.com/char0n/swagger-editor-validate"
      }
    </script>
  </a>
</div>

<h2 id="usage">Usage</h2>

<p>There are two significant use-cases of using this GitHub Action, but both of them use the fact that your definition
file lives in your GitHub repository. You just need to provide a file system path to it.</p>

<h3 id="public-use-case">Public use-case</h3>

<p>If you have access to the internet and don’t mind that this GitHub Action sends your OpenAPI definition to
<a href="https://editor.swagger.io/" target="_blank" rel="noopener noreferrer">https://editor.swagger.io/</a> for validation, then use this workflow.</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="na">on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">push</span><span class="pi">]</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">test_swagger_editor_validator_remote</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Swagger Editor Validator Remote</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Validate OpenAPI definition</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">char0n/swagger-editor-validate@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">definition-file</span><span class="pi">:</span> <span class="s">examples/openapi-2-0.yaml</span>
</pre></td></tr></tbody></table></code></pre></figure>

<h3 id="private-use-case">Private use-case</h3>

<p>If you want to maintain complete privacy and your OpenAPI definition may contain sensitive information 
use the following workflow. The workflow uses a swagger-editor docker image that runs as a service of the workflow.</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="code"><pre><span class="na">on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">push</span><span class="pi">]</span>


<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">test_swagger_editor_validator_service</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">Swagger Editor Validator Service</span>

    <span class="c1"># Service containers to run with `runner-job`</span>
    <span class="na">services</span><span class="pi">:</span>
      <span class="c1"># Label used to access the service container</span>
      <span class="na">swagger-editor</span><span class="pi">:</span>
        <span class="c1"># Docker Hub image</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">swaggerapi/swagger-editor</span>
        <span class="na">ports</span><span class="pi">:</span>
          <span class="c1"># Maps port 8080 on service container to the host 80</span>
          <span class="pi">-</span> <span class="s">80:8080</span>


    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v2</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Validate OpenAPI definition</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">char0n/swagger-editor-validate@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">swagger-editor-url</span><span class="pi">:</span> <span class="s">http://localhost/</span>
          <span class="na">definition-file</span><span class="pi">:</span> <span class="s">examples/openapi-2-0.yaml</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>This is what you’ll see if your OpenAPI definition validates <strong>successfully</strong>.</p>

<figure class="figure">
  <img src="/assets/img/blog/swagger-editor-validate-success.webp" width="1149" height="365" class="figure-img rounded mx-auto d-block img-fluid" alt="Successful validation" />
  <figcaption class="figure-caption text-center">Successful validation</figcaption>
</figure>

<p>This is what you’ll see if your OpenAPI definition contains <strong>errors</strong>.</p>

<figure class="figure">
  <img src="/assets/img/blog/swagger-editor-validate-errors.webp" width="1218" height="430" class="figure-img rounded mx-auto d-block img-fluid" alt="Failed validation" />
  <figcaption class="figure-caption text-center">Failed validation</figcaption>
</figure>

<h2 id="future">Future</h2>

<p>During implementations, I identified other features that this action would benefit from.
To not extend the implementation scope of the first version, I decided not to implement these features but instead document them and implement them later.
These features include:</p>

<ul>
  <li><a href="https://github.com/char0n/swagger-editor-validate/issues/1" target="_blank" rel="noopener noreferrer">Providing OpenAPI definition as a string</a></li>
  <li><a href="https://github.com/char0n/swagger-editor-validate/issues/2" target="_blank" rel="noopener noreferrer">Providing OpenAPI definition as URL</a></li>
  <li><a href="https://github.com/char0n/swagger-editor-validate/issues/4" target="_blank" rel="noopener noreferrer">Expose a screenshot on failure</a></li>
  <li><a href="https://github.com/char0n/swagger-editor-validate/issues/3" target="_blank" rel="noopener noreferrer">The mechanism for ignoring errors</a></li>
</ul>

<p>I think the most interesting one is - <strong>Mechanism for ignoring errors</strong>. Commonly, OpenAPI definitions 
contain errors when validated in <a href="https://editor.swagger.io/" target="_blank" rel="noopener noreferrer">https://editor.swagger.io/</a>. There are cases when there is nothing authors can do 
with these errors, which are known and ignored. This will open a door for such a use-case,
which I’m sure is quite common.</p>

<p>Hope that <a href="https://github.com/char0n/swagger-editor-validate" target="_blank" rel="noopener noreferrer">swagger-editor-validate</a> will become a handy tool for anybody using Swagger Editor to edit OpenAPI definitions.
I do know it will become convenient for my old team in Ubiquiti Inc ;] Please leave any feedback in the comments. I’d highly appreciate it!</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Building a GitHub Action workflow that uses Swagger Editor to validate an OpenAPI definition.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/swagger-editor-validate.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/swagger-editor-validate.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How Swagger Adjust can help you build extensible React+Redux apps</title><link href="https://vladimirgorej.com/blog/how-swagger-adjust-can-help-you-build-extensible-react-redux-apps/" rel="alternate" type="text/html" title="How Swagger Adjust can help you build extensible React+Redux apps" /><published>2021-02-25T08:39:04+00:00</published><updated>2021-02-25T08:39:04+00:00</updated><id>https://vladimirgorej.com/blog/how-swagger-adjust-can-help-you-build-extensible-react-redux-apps</id><content type="html" xml:base="https://vladimirgorej.com/blog/how-swagger-adjust-can-help-you-build-extensible-react-redux-apps/"><![CDATA[<p class="lead">
  Step by step guide to building React-Redux apps using pluggable architecture. Features are produced as plugins and are later composed to create the app. Plugins can be removed, added, or amended by other plugins.
</p>

<p>More than four years ago, I worked on a backend project where I needed to render HTML
documentation from <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md" target="_blank" rel="noopener noreferrer">OpenApi 2.0</a> JSON document defining our REST API written in hapi.js.
The obvious choice was, of course, SwaggerUI. <a href="https://hapi.dev/" target="_blank" rel="noopener noreferrer">hapi.js</a> ecosystem came with already implemented
integrations for OpenApi 2.0 and <a href="https://swagger.io/tools/swagger-ui/" target="_blank" rel="noopener noreferrer">SwaggerUI</a>. We just needed to use them, and we did. 
It all worked like a charm. At that time, I successfully got out of the complex
<a href="https://devdocs.io/angularjs~1.2/" target="_blank" rel="noopener noreferrer">Angular 1.2/2</a> world and got into the simple and more predictable world of <a href="https://reactjs.org/" target="_blank" rel="noopener noreferrer">React</a>+<a href="https://redux.js.org/" target="_blank" rel="noopener noreferrer">Redux</a>. 
I figured out that SwaggerUI was built in React, so I navigated to its <a href="https://github.com/swagger-api/swagger-ui" target="_blank" rel="noopener noreferrer">GitHub repo</a> and 
read the code to learn how large-scale React apps are written. The code was
different from the code I was used to writing. I built React applications using classical
patterns like <a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0" target="_blank" rel="noopener noreferrer">Presentational and Container Components</a> (smart &amp; dump components) and <a href="https://github.com/erikras/ducks-modular-redux" target="_blank" rel="noopener noreferrer">ducks pattern</a>.
It took me some time to understand that SwaggerUI was primarily built for extensibility in mind. 
Everything inside the code was revolves around this primary idea. The codebase was composed of several
plugins. These plugins were either adding new behaviors to SwaggerUI or enhancing
other behaviors defined in different plugins. There were almost no static imports involved 
because it had its own flavor of DI-container called <strong>System</strong>. Although the architecture looked 
interesting, it seemed overkill to use something like that in my current project.</p>

<p>In 2017, I was employed by a company called <a href="https://apiary.io/" target="_blank" rel="noopener noreferrer">Apiary</a>. I joined it a couple of months after
the acquisition by <a href="https://www.oracle.com/" target="_blank" rel="noopener noreferrer">Oracle</a>, and my primary task was to architect and develop a new version
of Apiary Documentation Renderer named: <strong>ApiaryUI</strong>. When I started to work on it, I remembered
my study on SwaggerUI and its unique architecture. I built ApiaryUI around the same ideas
of external extensibility. Of course, the code was completely different, but the architecture
was very similar to SwaggerUI. The core of ApiaryUI was based on a DI-container called <strong>Kernel</strong>.
I managed to finish the new <a href="https://blogs.oracle.com/developers/apiaryui" target="_blank" rel="noopener noreferrer">ApiaryUI</a> on time. Unfortunately, it has never been released as OpenSource
due to different political and organizational reasons. Still, it’s been utilized by Apiary
in its <a href="https://apiary.io/" target="_blank" rel="noopener noreferrer">web app</a> and used throughout various Oracle organizations.</p>

<p>Today I work for <a href="https://smartbear.com/" target="_blank" rel="noopener noreferrer">SmartBear</a>, the company that’s behind SwaggerUI and the entire Swagger ecosystem.
My primary focus in SmartBear is not on SwaggerUI, but from the moment I joined the company,
I started to think about improving and modernizing its DI-container (System). I’ve been actually
thinking about this for a long time. I started writing this article almost 3 years ago,
when I decoupled its DI-container for the first time as I needed it for a small project
with external extensibility requirements. Unfortunately, I never finished the article and 
never published the decoupled SwaggerUI DI-container as an OpenSource.</p>

<h2 id="swagger-adjust">Swagger Adjust</h2>

<p>But it’s all changing now. Let me introduce <a href="https://github.com/char0n/swagger-adjust" target="_blank" rel="noopener noreferrer">Swagger Adjust</a>  - a plugin-able framework for
building extensible React+Redux applications. This framework was born by decoupling ideas of 
the plugin-able core of SwaggerUI into a separate reusable component. It allows you to produce React+Redux applications where every component,
being a React Component, Redux state slice composed of actions, selector, reducers, or general business logic, is override-able and extensible.</p>

<p>Swagger Adjust is a wholly rewritten SwaggerUI’s DI-container (System) built on
the <a href="https://redux-toolkit.js.org/" target="_blank" rel="noopener noreferrer">Redux Toolkit</a>. The name for the DI-container is the same as SwaggerUI’s – <strong>System</strong>. 
Generally, the API of Swagger Adjust  is backward compatible with SwaggerUI.
When I say “generally”, I mean it in a way that the default configuration of Swagger Adjust 
is incompatible with SwaggerUI but can be adapted by configuration to be fully compatible.
Swagger Adjust is a free and Open Source software. It comes with a dual license – Apache License Version 2.0 is used for original code fragments, 
documentation fragments, and original ideas from SwaggerUI, and BSD 3-Clause License is used for everything else.</p>

<p>To demonstrate Swagger Adjust power, I’ve built a <a href="https://char0n.github.io/swagger-adjust/" target="_blank" rel="noopener noreferrer">simple TodoList app</a> composed of two plugins:
<a href="https://github.com/char0n/swagger-adjust/tree/main/demo/src/todo-list" target="_blank" rel="noopener noreferrer">todoList</a> and <a href="https://github.com/char0n/swagger-adjust/tree/main/demo/src/todo-list-enhancer" target="_blank" rel="noopener noreferrer">todoListEnhancer</a>. todoList plugin has the basic functionality of displaying a list of TodoItems 
and adding a new one. todoListEnhancer adds additional functionality like completion, deletion, and also 
batch operations on the entire TodoList.</p>

<figure class="figure d-block">
  <img src="/assets/img/blog/swagger-adjust-todo-list-plugin.webp" width="533" height="256" class="figure-img rounded mx-auto d-block img-fluid" alt="todoList plugin" />
  <figcaption class="figure-caption text-center">todoList plugin</figcaption>
</figure>

<p>todoList plugin looks like this when it renders. It contains AppBar, input for creating new todo
items, and a list of already added todo items.</p>

<figure class="figure d-block clearfix">
  <img src="/assets/img/blog/swagger-adjust-todo-list-plugin-dir-structure.webp" width="230" height="242" class="float-md-end" alt="todoList plugin" />
  <figcaption class="figure-caption">
    The directory structure of todoList plugin looks pretty standard. There is a components directory containing all the React Components.
    Redux-related code is concentrated in: actions.js, selectors.js, and reducers.js.
    Source code of these Redux files is unaware that it's part of a plugging-able DI-container and are built in the same way as they'd be built using Redux Toolkit.
    Please take a moment here and <a href="https://github.com/char0n/swagger-adjust/tree/main/demo/src/todo-list" target="_blank" rel="noopener noreferrer">browse these files</a> yourself to see the actual code. 
  </figcaption>
</figure>

<p>The only non-standard file is <a href="https://github.com/char0n/swagger-adjust/blob/main/demo/src/todo-list/plugin.js" target="_blank" rel="noopener noreferrer">plugin.js</a>. This file imports everything that todoPlugin consists of and
exports a function returning an <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#format" target="_blank" rel="noopener noreferrer">object with a specialized shape</a> that our DI-container recognizes.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
</pre></td><td class="code"><pre><span class="k">import</span> <span class="nx">TodoListLayout</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListLayout</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoListAppBar</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListAppBar</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoListInput</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListInput</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoList</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoList</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoListItem</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListItem</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">addItem</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./actions</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">selectTodoList</span><span class="p">,</span> <span class="nx">selectTodoListItems</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./selectors</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">reducers</span><span class="p">,</span> <span class="p">{</span> <span class="nx">initialState</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./reducers</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">TodoListPlugin</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">{</span>
    <span class="na">components</span><span class="p">:</span> <span class="p">{</span>
      <span class="nx">TodoListLayout</span><span class="p">,</span>
      <span class="nx">TodoListAppBar</span><span class="p">,</span>
      <span class="nx">TodoListInput</span><span class="p">,</span>
      <span class="nx">TodoList</span><span class="p">,</span>
      <span class="nx">TodoListItem</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">fn</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">todoList</span><span class="p">:</span> <span class="p">{},</span>
    <span class="p">},</span>
    <span class="na">statePlugins</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">todoList</span><span class="p">:</span> <span class="p">{</span>
        <span class="nx">initialState</span><span class="p">,</span>
        <span class="na">actions</span><span class="p">:</span> <span class="p">{</span> <span class="nx">addItem</span> <span class="p">},</span>
        <span class="na">selectors</span><span class="p">:</span> <span class="p">{</span> <span class="nx">selectTodoList</span><span class="p">,</span> <span class="nx">selectTodoListItems</span> <span class="p">},</span>
        <span class="nx">reducers</span><span class="p">,</span>
      <span class="p">},</span>
    <span class="p">},</span>
  <span class="p">};</span>
<span class="p">};</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">TodoListPlugin</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Now, this is where the real magic happens.
Instead of using static imports in React components, we use specialized <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/usage/hooks-api.md" target="_blank" rel="noopener noreferrer">hooks</a> to get everything we need from DI-container.
For the TodoList component to correctly render, we need to select all todo items from the state and then use the TodoListItem
component to render them.</p>

<figure class="highlight"><pre><code class="language-jsx" data-lang="jsx"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</pre></td><td class="code"><pre><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">List</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@material-ui/core/List</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">Divider</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@material-ui/core/Divider</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">makeStyles</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@material-ui/core/styles</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">useSystemComponent</span><span class="p">,</span> <span class="nx">useSystemSelector</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">swagger-adjust</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">useStyles</span> <span class="o">=</span> <span class="nx">makeStyles</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">({</span>
  <span class="na">root</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">paddingTop</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
    <span class="na">paddingBottom</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">}));</span>

<span class="kd">const</span> <span class="nx">TodoList</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">classes</span> <span class="o">=</span> <span class="nx">useStyles</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">items</span> <span class="o">=</span> <span class="nx">useSystemSelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">todoList</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">selectTodoListItems</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">TodoListItem</span> <span class="o">=</span> <span class="nx">useSystemComponent</span><span class="p">(</span><span class="dl">'</span><span class="s1">TodoListItem</span><span class="dl">'</span><span class="p">);</span>

  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nc">List</span> <span class="na">className</span><span class="p">=</span><span class="si">{</span><span class="nx">classes</span><span class="p">.</span><span class="nx">root</span><span class="si">}</span><span class="p">&gt;</span>
      <span class="si">{</span><span class="nx">items</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">item</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
        <span class="p">&lt;</span><span class="nc">React</span><span class="p">.</span><span class="nc">Fragment</span> <span class="na">key</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="p">&gt;</span>
          <span class="p">&lt;</span><span class="nc">TodoListItem</span> <span class="na">item</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="si">}</span> <span class="p">/&gt;</span>
          <span class="p">&lt;</span><span class="nc">Divider</span> <span class="p">/&gt;</span>
        <span class="p">&lt;/</span><span class="nc">React</span><span class="p">.</span><span class="nc">Fragment</span><span class="p">&gt;</span>
      <span class="p">))</span><span class="si">}</span>
    <span class="p">&lt;/</span><span class="nc">List</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">};</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">TodoList</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>DI-container has its own <a href="https://reactjs.org/docs/context.html" target="_blank" rel="noopener noreferrer">React Context</a> called <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/usage/api.md#systemcontext" target="_blank" rel="noopener noreferrer">SystemContext</a>, which accepts its instance. 
Tne first thing we do is – compose a list of plugins and pass it to the System class constructor
(represents DI-container). DI-container automatically compiles the plugins under the hood and 
creates a store instance for us. Then we wrap our main App component into <a href="https://react-redux.js.org/api/provider" target="_blank" rel="noopener noreferrer">react-redux</a> and
SystemContext providers. This allows hooks mentioned above to access anything from the DI-container 
within React components.</p>

<figure class="highlight"><pre><code class="language-jsx" data-lang="jsx"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre><span class="cm">/**
 * This component is just responsible to render the main TodoList component.
 * We need to this to allow other plugins to override TodoListLayout.
 */</span>

<span class="kd">const</span> <span class="nx">App</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">TodoListLayout</span> <span class="o">=</span> <span class="nx">useSystemComponent</span><span class="p">(</span><span class="dl">'</span><span class="s1">TodoListLayout</span><span class="dl">'</span><span class="p">);</span>

  <span class="k">return</span> <span class="p">&lt;</span><span class="nc">TodoListLayout</span> <span class="p">/&gt;;</span>
<span class="p">};</span>

<span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span>
  <span class="p">&lt;</span><span class="nc">React</span><span class="p">.</span><span class="nc">StrictMode</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nc">Provider</span> <span class="na">store</span><span class="p">=</span><span class="si">{</span><span class="nx">store</span><span class="si">}</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nc">SystemContext</span><span class="p">.</span><span class="nc">Provider</span> <span class="na">value</span><span class="p">=</span><span class="si">{</span><span class="nx">system</span><span class="p">.</span><span class="nx">getSystem</span><span class="si">}</span><span class="p">&gt;</span>
        <span class="p">&lt;</span><span class="nc">App</span> <span class="p">/&gt;</span>
      <span class="p">&lt;/</span><span class="nc">SystemContext</span><span class="p">.</span><span class="nc">Provider</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nc">Provider</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nc">React</span><span class="p">.</span><span class="nc">StrictMode</span><span class="p">&gt;,</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">root</span><span class="dl">'</span><span class="p">)</span>
<span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Let’s look into our second plugin – todoListEnhancer. As mentioned before, this plugin enhances the 
original todoList plugin and adds additional features. Enhancement doesn’t happen via todoList 
plugin code modification but rather by its configuration. And this is where Swagger Adjust really shines.</p>

<figure class="figure d-block">
  <img src="/assets/img/blog/swagger-adjust-todo-list-enhancer-plugin.webp" width="533" height="256" class="figure-img rounded mx-auto d-block img-fluid" alt="todoListEnhancer plugin" />
  <figcaption class="figure-caption text-center">todoListEnhancer plugin</figcaption>
</figure>

<p>Every todo item in todoListEnhancer is now associated with creation time, the checkbox for completion, 
the bin icon for deletion, and at the bottom of TodoList, there are two buttons for batch operations.</p>

<p>Here, we’re presented with todoListEnhancer directory structure and the source code of plugin.js.
Again, please take a moment here and <a href="https://github.com/char0n/swagger-adjust/tree/main/demo/src/todo-list-enhancer" target="_blank" rel="noopener noreferrer">browse these files</a> yourself to see the actual code.
Inside <a href="https://github.com/char0n/swagger-adjust/blob/main/demo/src/todo-list-enhancer/plugin.js" target="_blank" rel="noopener noreferrer">plugin.js</a> we’re replacing the TodoListItem component with the new one. We’re adding
the new TodoListBatchOperations component and wrapping the TodoList component with a new one
that composes the original TodoList component with  TodoListBatchOperations.
Additionally, we’re exposing the time formatting function to the plugin so that additional plugins
could easily override how time is formatted.</p>

<figure class="figure d-block">
  <img src="/assets/img/blog/swagger-adjust-todo-list-enhancer-plugin-dir-structure.webp" width="252" height="202" class="figure-img rounded d-block img-fluid" alt="todoListEnhancer plugin directory structure" />
</figure>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="code"><pre><span class="k">import</span> <span class="p">{</span> <span class="nx">assocPath</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">ramda</span><span class="dl">'</span><span class="p">;</span>

<span class="k">import</span> <span class="p">{</span> <span class="nx">completeItem</span><span class="p">,</span> <span class="nx">uncompleteItem</span><span class="p">,</span> <span class="nx">deleteItem</span><span class="p">,</span> <span class="nx">completeAll</span><span class="p">,</span> <span class="nx">deleteAll</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./actions</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">formatTimestamp</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./fn</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">reducers</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./reducers</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoList</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoList</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoListItem</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListItem</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">TodoListBatchOperations</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./components/TodoListBatchOperations</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">TodoListEnhancerPlugin</span> <span class="o">=</span> <span class="p">(</span><span class="nx">system</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">todoListFn</span> <span class="o">=</span> <span class="nx">assocPath</span><span class="p">([</span><span class="dl">'</span><span class="s1">formatTimestamp</span><span class="dl">'</span><span class="p">],</span> <span class="nx">formatTimestamp</span><span class="p">,</span> <span class="nx">system</span><span class="p">.</span><span class="nx">fn</span><span class="p">.</span><span class="nx">todoList</span><span class="p">);</span>

  <span class="k">return</span> <span class="p">{</span>
    <span class="na">components</span><span class="p">:</span> <span class="p">{</span>
      <span class="nx">TodoListItem</span><span class="p">,</span>
      <span class="nx">TodoListBatchOperations</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">wrapComponents</span><span class="p">:</span> <span class="p">{</span>
      <span class="nx">TodoList</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">fn</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">todoList</span><span class="p">:</span> <span class="nx">todoListFn</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="na">statePlugins</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">todoList</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">actions</span><span class="p">:</span> <span class="p">{</span>
          <span class="nx">completeItem</span><span class="p">,</span>
          <span class="nx">uncompleteItem</span><span class="p">,</span>
          <span class="nx">deleteItem</span><span class="p">,</span>
          <span class="nx">completeAll</span><span class="p">,</span>
          <span class="nx">deleteAll</span><span class="p">,</span>
        <span class="p">},</span>
        <span class="na">wrapActions</span><span class="p">:</span> <span class="p">{</span>
          <span class="na">addItem</span><span class="p">:</span> <span class="p">(</span><span class="nx">oriAction</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="nx">oriAction</span><span class="p">({</span> <span class="p">...</span><span class="nx">payload</span><span class="p">,</span> <span class="na">completed</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">createdAt</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="p">});</span>
          <span class="p">},</span>
        <span class="p">},</span>
        <span class="nx">reducers</span><span class="p">,</span>
      <span class="p">},</span>
    <span class="p">},</span>
  <span class="p">};</span>
<span class="p">};</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">TodoListEnhancerPlugin</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>The last thing we need to do is to register the todoListEnhancer plugin with the DI-Container.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="kd">const</span> <span class="nx">plugins</span> <span class="o">=</span> <span class="p">[</span><span class="nx">TodoListPlugin</span><span class="p">,</span> <span class="nx">TodoListEnhancerPlugin</span><span class="p">];</span>
<span class="kd">const</span> <span class="nx">system</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">System</span><span class="p">({</span> <span class="nx">plugins</span> <span class="p">});</span>
<span class="kd">const</span> <span class="nx">store</span> <span class="o">=</span> <span class="nx">system</span><span class="p">.</span><span class="nx">getStore</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>When building React+Redux features in this architecture, one always has to think hard about exposing
React components, Redux code, and other business or presentational logic to the plugin so that
other plugins can easily enhance it. This also works in reverse – when building features that need 
to be “protected” from enhancements, instead use classical static ES6 imports, which prohibits anybody
from enhancing your plugins.</p>

<h2 id="advanced-usage-patterns">Advanced usage patterns</h2>

<p>During writing Swagger Adjust and using it on different projects, I’ve identified
multiple advanced usage patterns that need to be mentioned.  These advanced usage patterns include:</p>

<ul>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#routing-patterns" target="_blank" rel="noopener noreferrer">various messaging patterns (Routing and Transformation) when wrapping actions</a></li>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#initial-state" target="_blank" rel="noopener noreferrer">providing initial state for a state plugin</a></li>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#reducers" target="_blank" rel="noopener noreferrer">override reducers from another state plugin</a></li>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#composing-selectors-from-single-plugin" target="_blank" rel="noopener noreferrer">composing selectors in single state plugin</a></li>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#composing-selectors-from-different-plugins" target="_blank" rel="noopener noreferrer">composing selectors from different plugins</a></li>
  <li><a href="https://github.com/char0n/swagger-adjust/blob/main/docs/customization/plugin-api.md#hooks" target="_blank" rel="noopener noreferrer">registering and using hooks</a></li>
</ul>

<p>Swagger Adjust consumers need to understand these usage patterns properly as they set conventions on how Swagger Adjust was designed to be used.</p>

<h2 id="word-on-conventions">Word on conventions</h2>

<p>Conventions are essential when producing any code. When utilized properly, it makes code 
produced by different people look like it was produced by a single person.
Redux Toolkit has done a great job setting up conventions and standards for React+Redux applications.
Swagger Adjust adopts all these conventions and adds a couple of its own:</p>

<ul>
  <li>use <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/usage/api.md#createaction" target="_blank" rel="noopener noreferrer">createAction</a> helper to create action creators</li>
  <li>use following convention for action type: “namespace/action”</li>
  <li>use <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/usage/api.md#createasyncthunk" target="_blank" rel="noopener noreferrer">createAsyncThunk</a> to create async actions or handle side effects</li>
  <li>structure Files as Feature Folders as shown in this article</li>
  <li>name your selectors in the following naming scheme: <code class="language-plaintext highlighter-rouge">select&lt;Name&gt;</code></li>
  <li>create memoized selectors using <a href="https://github.com/char0n/swagger-adjust/blob/main/docs/usage/api.md#createselector" target="_blank" rel="noopener noreferrer">createSelector</a> utility</li>
</ul>

<p>Consult <a href="https://redux.js.org/style-guide/style-guide" target="_blank" rel="noopener noreferrer">Redux Style Guide</a> for additional conventions.</p>

<h2 id="closing-words">Closing words</h2>

<p>Swagger Adjust it’s an ideal framework for creating User Interfaces (UI) with external extensibility
requirement. It should work pretty well for creating renderers for different specifications like OpenApi,
AsyncApi, or ApiBlueprint (as SwaggerUI demonstrated). It allows external extensibility via configuration 
and plugins, bringing much more features than demonstrated in this article. 
I welcome you to consult our documentation to learn more.</p>

<p>You can find all code demonstrated in this article in the <a href="https://github.com/char0n/swagger-adjust" target="_blank" rel="noopener noreferrer">Swagger Adjust GitHub repository</a>.</p>

<div class="list-group mb-3">
  <a href="https://github.com/char0n/swagger-adjust" class="list-group-item list-group-item-action" target="_blank" rel="noopener noreferrer">
    <div class="d-flex w-100 justify-content-between">
      <h2 class="h5 mb-1"><i class="fa-brands fa-github"></i> swagger-adjust</h2>
    </div>
    <blockquote class="blockquote fs-6 mb-1">
      Pluggable framework for creating extendable React+Redux applications.
    </blockquote>
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "SoftwareSourceCode",
        "author": { "@id": "https://vladimirgorej.com" },
        "name": "swagger-adjust",
        "abstract": "Pluggable framework for creating extendable React+Redux applications.",
        "codeRepository": "https://github.com/char0n/swagger-adjust"
      }
    </script>
  </a>
</div>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Step by step guide to building React-Redux apps using pluggable architecture. Features are produced as plugins and are later composed to create the app. Plugins can be removed, added, or amended by other plugins.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://vladimirgorej.com/assets/img/blog/swagger-adjust.webp" /><media:content medium="image" url="https://vladimirgorej.com/assets/img/blog/swagger-adjust.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>