<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Backend Cafe]]></title><description><![CDATA[A blog to discuss about Node.js Backend development. It focuses on Fastify web framework to help the community to skill up and build secure RESTful API faster.]]></description><link>https://backend.cafe</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1634463829513/Phzj8p_Tg.png</url><title>Backend Cafe</title><link>https://backend.cafe</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 15 May 2026 00:59:09 GMT</lastBuildDate><atom:link href="https://backend.cafe/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[React Server-Side Streaming with Fastify]]></title><description><![CDATA[React 18+ introduced renderToPipeableStream, a powerful API for streaming server-rendered HTML to the browser. Instead of waiting for the entire page to render before sending it to the client, you can]]></description><link>https://backend.cafe/react-server-side-streaming-with-fastify</link><guid isPermaLink="true">https://backend.cafe/react-server-side-streaming-with-fastify</guid><category><![CDATA[fastify]]></category><category><![CDATA[React]]></category><category><![CDATA[streaming]]></category><category><![CDATA[StreamingData]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Mon, 16 Mar 2026 09:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/616ae2ddbfbbca677ca1fd12/7753ccf0-8029-4c95-b622-9be853bfbe67.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>React 18+ introduced <code>renderToPipeableStream</code>, a powerful API for streaming server-rendered HTML to the browser. Instead of waiting for the entire page to render before sending it to the client, you can start sending HTML as soon as it's ready.</p>
<p>In this article, we'll build a minimal Fastify server that streams a React component to the browser using Node.js streams. No build tools, no TypeScript — just a simple setup you can run in seconds.</p>
<h2>Setting up the project</h2>
<p>We'll use Node.js 20+ and Fastify v5 for this tutorial. Since we're using <code>React.createElement</code> directly, we don't need JSX or any build step.</p>
<pre><code class="language-bash">mkdir fastify-react-streaming
cd fastify-react-streaming

npm init es6 --yes

npm install fastify@5 react@19 react-dom@19
</code></pre>
<h2>How does reply.hijack() work?</h2>
<p>By default, Fastify manages the response lifecycle for you: it serializes your return value, sets headers, and sends the response. But sometimes you need full control over the raw Node.js <code>http.ServerResponse</code>, for example when you want to pipe a stream directly to the client.</p>
<p>That's what <code>reply.hijack()</code> does. When you call it, Fastify steps aside and hands you the raw response object via <code>reply.raw</code>.</p>
<p>From that point on, <strong>you</strong> are responsible for writing headers, sending data, and ending the response.</p>
<p>This is exactly what we need for React streaming: <code>renderToPipeableStream</code> expects a writable Node.js stream to pipe into, and <code>reply.raw</code> is just that.</p>
<h2>Streaming a React component</h2>
<p>Create a file named <code>app.js</code>:</p>
<pre><code class="language-js">import Fastify from 'fastify'
import React from 'react'
import { renderToPipeableStream } from 'react-dom/server'

const app = Fastify({ logger: true })

app.get('/', async (req, reply) =&gt; {
  reply.hijack()

  const stream = renderToPipeableStream(
    React.createElement('div', null, 'Hello from React streaming!'),
    {
      onShellReady () {
        reply.raw.setHeader('Content-Type', 'text/html; charset=utf-8')
        stream.pipe(reply.raw)
      },
      onShellError (err) {
        reply.raw.statusCode = 500
        reply.raw.setHeader('Content-Type', 'text/html; charset=utf-8')
        reply.raw.end('&lt;!doctype html&gt;&lt;p&gt;Something went wrong&lt;/p&gt;')
        console.error(err)
      },
      onError (err) {
        console.error(err)
      }
    }
  )
})

await app.listen({ port: 3000 })
</code></pre>
<p>Let's break down what's happening:</p>
<ol>
<li><p><code>reply.hijack()</code> tells Fastify we'll manage the response ourselves. Without this, Fastify would try to send its own response and conflict with our stream.</p>
</li>
<li><p><code>renderToPipeableStream</code> takes a React element and returns a stream object. We use <code>React.createElement</code> directly instead of JSX, so no build step is needed.</p>
</li>
<li><p><code>onShellReady</code> fires when the initial HTML shell is ready to be sent. At this point, we set the <code>Content-Type</code> header and pipe the stream into <code>reply.raw</code> — the underlying Node.js response.</p>
</li>
<li><p><code>onShellError</code> fires if the initial shell fails to render — before any HTML has been sent to the client. Since the response hasn't started yet, we can still set a proper status code and send a fallback HTML page. This is the right place to handle fatal rendering failures.</p>
</li>
<li><p><code>onError</code> handles errors that occur <em>after</em> the shell has been sent and streaming has begun. At that point, headers and status code are already flushed, so we can only log the error.</p>
</li>
</ol>
<p>Start the server:</p>
<pre><code class="language-bash">node app.js
</code></pre>
<p>Open <code>http://localhost:3000</code> in your browser and you'll see the streamed HTML response.</p>
<h2>Why streaming matters</h2>
<p>For this simple example, the difference between streaming and a regular <code>renderToString</code> call is negligible. But streaming becomes important when your React tree includes:</p>
<ul>
<li><p><code>&lt;Suspense&gt;</code> <strong>boundaries</strong> with async data fetching: the shell is sent immediately while suspended parts stream in later.</p>
</li>
<li><p><strong>Large component trees</strong>: the browser can start parsing and displaying HTML before the server finishes rendering everything.</p>
</li>
</ul>
<p>The <code>onShellReady</code> callback is the key: it fires as soon as the content above any <code>&lt;Suspense&gt;</code> boundary is ready, letting the browser start rendering right away.</p>
<h2>Summary</h2>
<p>With just a few lines of code, we've set up React server-side streaming in Fastify using <code>reply.hijack()</code> and <code>renderToPipeableStream</code>. No bundlers, no JSX compilation! Just Node.js, React, and Fastify working together.</p>
<p>The source code for this project is available on <a href="https://github.com/Eomm/blog-posts/tree/HEAD/bonus/react-streaming/">GitHub</a>.</p>
<p>If you enjoyed this article, comment, share and follow me on <a href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Signing git commits with GPG]]></title><description><![CDATA[Did you see the "Verified" badge on some GitHub commits and wonder how to get it for your own commits?

Well,GitHub shows the ‘Verified’ badge when a commit has a valid signature from a trusted key (GPG, SSH, S/MIME, or GitHub’s own). In this article...]]></description><link>https://backend.cafe/signing-git-commits-with-gpg</link><guid isPermaLink="true">https://backend.cafe/signing-git-commits-with-gpg</guid><category><![CDATA[gpg]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sun, 18 Jan 2026 09:02:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-KWTvrNiYOE/upload/85b52a281d8029d323be6145e6d9cbfe.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Did you see the "Verified" badge on some GitHub commits and wonder how to get it for your own commits?</p>
<p><img src="https://raw.githubusercontent.com/Eomm/blog-posts/2eabac9febac635a361aaea46d7efd8d83eb2e8f/posts/assets/gpg-verified.png" alt="gpg-verified.png" /></p>
<p>Well,GitHub shows the ‘Verified’ badge when a commit has a valid signature from a trusted key (GPG, SSH, S/MIME, or GitHub’s own). In this article we’ll focus on the GPG path: generating a key pair, registering the public key with GitHub, matching the key’s email to your GitHub account, and configuring <code>git</code> to sign your commits so GitHub can verify them.</p>
<p>A signature proves the commit came from someone who controls a specific private key, instead of just "whatever machine happened to use your name and email" in the <code>git</code> config. For example, a dummy attack could be to commit code on a Pull Request on behalf of another developer, by using their name and email in the commit metadata to trick maintainers into thinking the commit is legitimate and merging it without proper review.</p>
<p>In fact, by simply changing the <code>git</code> config user name and email, you can impersonate anyone:</p>
<p><img src="https://raw.githubusercontent.com/Eomm/blog-posts/2eabac9febac635a361aaea46d7efd8d83eb2e8f/posts/assets/gpg-unverified.png" alt="gpg-unverified.png" /></p>
<p>With the "Verified" badge, you will get trust for your contributions and it defends against impersonation.</p>
<p>So, would you like to learn how to sign your git commits with GPG? Let's get started!</p>
<h2 id="heading-setup-gpg">Setup GPG</h2>
<p>First, you need to have <a target="_blank" href="https://www.gnupg.org/download/index.html">GPG (GNU Privacy Guard)</a> installed on your machine (available for Windows, macOS, and Linux). GPG is an open-source implementation of the OpenPGP standard used for public-key cryptography, primarily for encrypting data, signing data, and managing cryptographic keys.<br />It works by maintaining a keyring of public and private keys and applying <a target="_blank" href="https://en.wikipedia.org/wiki/Public-key_cryptography">asymmetric cryptography</a> for key exchange and identity verification, often combined with <a target="_blank" href="https://en.wikipedia.org/wiki/Symmetric-key_algorithm">symmetric cryptography</a> for actual data encryption.</p>
<p>The <code>gpg-agent</code> is a long-running background process that handles private key operations on GPG’s behalf: it securely caches decrypted private keys in memory, prompts for passphrases, integrates with smart cards or hardware tokens, and exposes a socket-based API so GPG and other tools don’t repeatedly access private key material. In practice, GPG delegates all sensitive key usage to <code>gpg-agent</code>, which reduces passphrase prompts, centralizes key protection, and limits how often private keys are decrypted.</p>
<p>That sounds complicated, but don't worry; once it is configured, it will work seamlessly in the background!</p>
<p>If you are using macOS, the easiest way to install GPG is by using <a target="_blank" href="https://brew.sh/">Homebrew</a>:</p>
<pre><code class="lang-bash">brew install gnupg
</code></pre>
<h3 id="heading-pinentry">Pinentry</h3>
<p>Another important component is the <a target="_blank" href="https://www.gnupg.org/related_software/pinentry/index.html"><code>pinentry</code> program</a>, which is a collection of simple user interface dialogs used by GPG and <code>gpg-agent</code> to securely prompt users for sensitive information, such as passphrases or PINs. It ensures that sensitive data is entered in a secure manner, preventing exposure to other applications or potential keyloggers.</p>
<p>To install it, you can follow the official website or use your system package manager again:</p>
<pre><code class="lang-bash">brew install pinentry-mac
</code></pre>
<p>To configure <code>gpg-agent</code> to use <code>pinentry</code>, you need to edit (or create) the <code>gpg-agent.conf</code> file located in the GPG home directory (usually <code>~/.gnupg/</code>):</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"pinentry-program /usr/local/bin/pinentry-mac"</span> &gt;&gt; ~/.gnupg/gpg-agent.conf
<span class="hljs-comment"># Restart gpg-agent to apply the changes</span>
gpgconf --<span class="hljs-built_in">kill</span> gpg-agent
</code></pre>
<blockquote>
<p>💡 The <code>~/.gnupg/gpg-agent.conf</code> file may include the <code>default-cache-ttl</code> and <code>max-cache-ttl</code> settings to control how long the passphrase is cached in memory</p>
</blockquote>
<p>Good, if you have GPG installed, the <code>gpg</code> command should be available in your terminal and we can start using it.</p>
<h2 id="heading-create-a-gpg-key-pair">Create a GPG key pair</h2>
<p>To sign your git commits, you need to create a GPG key pair (a public key and a private key). Creating a GPG key pair can be done using the following command:</p>
<pre><code class="lang-bash">gpg --full-generate-key
</code></pre>
<p>This will prompt you to select some options for your key pair; let's go through them briefly:</p>
<ul>
<li><p><strong>Key type</strong>: You can choose the default option by pressing Enter. Select the option based on the supported algorithms of your environment. In our case, ECC is a good choice because GitHub supports it</p>
</li>
<li><p><strong>Elliptic curve</strong>: You can choose the default option (<code>Curve 25519</code>) by pressing Enter</p>
</li>
<li><p><strong>Key expiration</strong>: You can choose how long the key will be valid. It is a good practice to set an expiration date for security reasons such as 1y (1 year) in case you stop using a laptop or device</p>
</li>
<li><p><strong>Real name</strong>: Enter your full name as you want it to appear in the commit signature</p>
</li>
<li><p><strong>Email address</strong>: Enter the email address associated with your GitHub account</p>
</li>
<li><p><strong>Comment</strong>: Add a comment to help you identify the key later</p>
</li>
<li><p><strong>Passphrase</strong>: Choose a strong passphrase to protect your private key. Don't forget it! Here we expect <code>pinentry</code> to prompt you for it.</p>
</li>
</ul>
<p>To list the keys you have created, you can use the following command:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># list private (secret) keys</span>
gpg --list-secret-keys --keyid-format=long
</code></pre>
<p>The output will look like this:</p>
<pre><code class="lang-bash">sec   ed25519/CACEF4B5F2457FA1 2026-01-17 [SC] [expires: 2027-01-17]
      3FDF83A1577BED43B90F0D63CACEF4B5F2457FA1
uid                 [ultimate] Backend Cafe (Demo cert) &lt;be@demo.com&gt;
ssb   cv25519/D732AC59B5E3FC30 2026-01-17 [E] [expires: 2027-01-17]
</code></pre>
<p>In this example, the GPG key ID is <code>CACEF4B5F2457FA1</code>, take note of it because we will need it later.</p>
<h3 id="heading-verify-gpg-signing-works">Verify GPG signing works</h3>
<p>To verify that GPG signing works, we can just sign a test message:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"test"</span> | gpg --default-key CACEF4B5F2457FA1 --sign --armor
</code></pre>
<p>This should prompt you for your passphrase (handled by <code>pinentry</code>) and output a signed message block like this:</p>
<pre><code class="lang-plaintext">gpg: using "CACEF4B5F2457FA1" as default secret key for signing
-----BEGIN PGP MESSAGE-----

owGbwMvMwCV26tyXrZ9c6xcynuZOYsjMXra[...truncated for brevity...]
V98zj+7xb4EP/2rw9pVWn3rMBAA===ma+I
-----END PGP MESSAGE-----
</code></pre>
<p>If everything works fine, you can proceed to the next step.</p>
<h2 id="heading-configure-github-to-verify-your-gpg-signatures">Configure GitHub to verify your GPG signatures</h2>
<p>To configure GitHub to verify your GPG signatures, you need to add your public GPG key to your GitHub account. First, export your public GPG key using the following command:</p>
<pre><code class="lang-bash">gpg --armor --<span class="hljs-built_in">export</span> CACEF4B5F2457FA1
</code></pre>
<p>This will output something like this:</p>
<pre><code class="lang-plaintext">-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEaWulshYJKwYBBAHaRw8B[...truncated for brevity...]
-----END PGP PUBLIC KEY BLOCK-----
</code></pre>
<p>Now copy the entire output (including the <code>-----BEGIN PGP PUBLIC KEY BLOCK-----</code> and <code>-----END PGP PUBLIC KEY BLOCK-----</code> lines) and go to your GitHub account settings at:</p>
<ul>
<li>Navigate to <strong>Settings</strong> &gt; <strong>SSH and GPG keys</strong> &gt; <strong>New GPG key</strong></li>
</ul>
<p>Or just go to this URL <a target="_blank" href="https://github.com/settings/gpg/new">https://github.com/settings/gpg/new</a>.</p>
<p>Paste your public GPG key into the "Key" field and click on the "Add GPG key" button to save it. Now GitHub will be able to verify your signed commits.</p>
<h2 id="heading-configure-git-to-sign-commits-by-default">Configure Git to sign commits by default</h2>
<p>The last step is to configure your local <code>git</code> to sign commits by default using your GPG key. You can do this by running the following commands:</p>
<pre><code class="lang-bash">git config --global user.signingkey CACEF4B5F2457FA1
git config --global commit.gpgSign <span class="hljs-literal">true</span>
git config --global gpg.program gpg
</code></pre>
<p>This will edit your <code>~/.gitconfig</code> file to include the following lines:</p>
<pre><code class="lang-ini"><span class="hljs-section">[user]</span>
    <span class="hljs-attr">name</span> = Demo cert
    <span class="hljs-attr">email</span> = be@demo.com
    <span class="hljs-attr">signingkey</span> = CACEF4B5F2457FA1
<span class="hljs-section">[commit]</span>
    <span class="hljs-attr">gpgsign</span> = <span class="hljs-literal">true</span>
<span class="hljs-section">[gpg]</span>
    <span class="hljs-attr">program</span> = gpg
</code></pre>
<h2 id="heading-test-it">Test it!</h2>
<p>Fantastic! Now everything is set up, and it is time to test it! Create a new git commit in any of your repositories and push the code to GitHub. After pushing, go to the commit page on GitHub, and you should see the "Verified" badge next to your commit message!</p>
<h2 id="heading-backup-your-gpg-keys">Backup your GPG keys</h2>
<p>It is crucial to back up your GPG keys, especially the private key, as losing it means you won't be able to sign commits and you will need to create a new key pair. You can back up your keys by exporting them to a file:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># export private (secret) key</span>
gpg --export-secret-keys --armor CACEF4B5F2457FA1 &gt; CACEF4B5F2457FA1-secret.asc
</code></pre>
<p>Make sure to store this file in a safe place, preferably in an offline location such as an encrypted USB drive.</p>
<p>To restore your keys from the backup file, you can use the following command:</p>
<pre><code class="lang-bash">gpg --import CACEF4B5F2457FA1-secret.asc
</code></pre>
<p>That's it!</p>
<h2 id="heading-summary">Summary</h2>
<p>This guide shows you how to get the “Verified” badge on your GitHub commits by generating a GPG key pair, configuring <code>gpg-agent</code> and <code>pinentry</code>, and registering your public key with GitHub.</p>
<p>It walks through creating and testing your key, setting <code>git</code> to sign commits automatically, and safely backing up your private key so you don’t lose your signing identity.</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://x.com/ManuEomm">X</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Beyond Vibe Coding]]></title><description><![CDATA[Marcus gets a "quick fix" request from his product manager. The user authentication flow needs "just a small tweak", maybe an hour of work. He fires up his AI coding assistant and starts exploring the auth module.
Three hours later, Marcus has rewrit...]]></description><link>https://backend.cafe/beyond-vibe-coding</link><guid isPermaLink="true">https://backend.cafe/beyond-vibe-coding</guid><category><![CDATA[vibe coding]]></category><category><![CDATA[AI]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[claude.ai]]></category><category><![CDATA[claude-code]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Maksim Sinik]]></dc:creator><pubDate>Thu, 07 Aug 2025 17:03:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754579399596/0ff7156b-8ab8-4741-bd94-b234c42f7806.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Marcus gets a "quick fix" request from his product manager. The user authentication flow needs "just a small tweak", maybe an hour of work. He fires up his AI coding assistant and starts exploring the auth module.</p>
<p>Three hours later, Marcus has rewritten two components, discovered a hidden dependency that breaks when touched, and created a bug that affects the shopping cart in an unexpected way. His AI assistant has been helpful, but keeps suggesting solutions that create new problems. The "quick fix" is now a two-day refactor, and nobody understands why.</p>
<p>Meanwhile, Sarah, on the same team, receives a similar request. But Sarah works differently. She spends time analyzing the authentication system's history, documenting potential ripple effects, and mapping out an implementation strategy before writing any code. Only then does she engage her AI assistant, but now with rich context about business constraints, technical dependencies, and architectural patterns. The result: a clean solution written in thirty minutes, almost entirely by the AI, that integrates seamlessly with existing systems.</p>
<p>Same team. Same AI tools. Completely different outcomes.</p>
<p>The difference? Sarah treats her AI assistant as a collaborative partner in understanding, not just a code generator. She's evolved beyond <strong>vibe coding</strong>.</p>
<h2 id="heading-when-ai-amplifies-chaos"><strong>When AI Amplifies Chaos</strong></h2>
<p>Vibe coding isn't new. Developers have been copying and pasting from Stack Overflow for years. However, AI has made this dangerous: you can now transition from a gut feeling to production bugs in seconds, not hours.</p>
<p><em>"I'll figure it out as I go."</em></p>
<p><em>"This feels like the right approach."</em></p>
<p><em>"Let's just try it and see what happens."</em></p>
<p>These phrases define vibe coding, development driven by <strong>intuition</strong> and <strong>reactive problem-solving</strong>, as well as whatever knowledge happens to be in someone's head at the moment.</p>
<p>Here's the problem: <strong>AI makes bad practices faster</strong>, not better practices easier. When you prompt an AI with "make this work," you get code that works in isolation but breaks everything else. When your context is "fix this bug," you get a fix that creates three new bugs.</p>
<p>This is where the evolution matters. AI tools excel at generating code quickly. They struggle to understand why that code matters, what it affects, and how it fits into the larger system. The key insight is that <strong>without structured context management, your AI assistant becomes a speedy way to create very sophisticated technical debt</strong>.</p>
<p>However, when you evolve your approach, moving from vibe coding to systematic context building, AI becomes something entirely different: a partner that amplifies good practices instead of accelerating bad ones. How do you evolve from Marcus's chaotic approach to Sarah's systematic method? How do you build that kind of contextual thinking?</p>
<h2 id="heading-my-journey-discovering-context-by-evolution"><strong>My Journey: Discovering Context by Evolution</strong></h2>
<p>Two months ago, I was Marcus. I'd get a feature request, fire up Claude Code, and start coding immediately. Sometimes it worked brilliantly. Sometimes I'd create more problems than I solved.</p>
<p>My first breakthrough came when I realized I needed better implementation patterns. I created a specialized "executor" agent, a Claude session focused solely on writing clean, consistent code. This helped, but I continued to encounter architectural problems.</p>
<p>So I built a "planner" agent to design before implementing. Now I had two agents: one for planning and one for execution. Better, but still missing something crucial. My plans kept hitting unexpected constraints because I didn't understand the existing system well enough.</p>
<p>That led to my "discoverer" agent, designed to understand legacy systems and business context before planning began. Three agents working in sequence: discover, plan, execute. Each one created a richer context that the next consumed.</p>
<p>But complex features needed more than this linear flow. I found myself needing strategic analysis upfront, systematic work breakdown, and ongoing project monitoring. What started as a single "executor" agent evolved into six specialized reasoning capabilities, each designed to excel in a specific type of contextual thinking.</p>
<p>This evolution taught me something profound: I wasn't just building better AI tools for myself, I was discovering a systematic approach to managing context throughout development. What I now call <strong>Contextual Intelligence</strong>.</p>
<h2 id="heading-enter-contextual-intelligence"><strong>Enter Contextual Intelligence</strong></h2>
<p>What if your AI assistant knew not just what to build, but why you're building it, how it connects to existing systems, and what success actually looks like?</p>
<p><strong>Contextual Intelligence</strong> is the disciplined practice of ensuring the correct information reaches the right decisions at the right time. Instead of losing context between phases, you deliberately capture, enrich, and transfer understanding so that each development decision builds on accumulated knowledge rather than fragmented assumptions.</p>
<p>This isn't about having smarter AI tools or better prompts. It's about fundamentally changing how we structure the relationship between human insight and AI capability. Instead of treating AI as a better search engine or a faster code generator, contextual Intelligence treats AI as a collaborative partner in building and maintaining understanding.</p>
<p>Every development decision requires context, but we treat context like magic. It exists in someone's head, gets lost in Slack threads, or lives in comments that nobody remembers writing. Rebuilding a fragmented context can lead to poor decisions, resulting in subpar code and accumulating technical debt.</p>
<p><strong>Contextual Intelligence operates on four core principles:</strong></p>
<ul>
<li><p><strong>Capturing</strong>: Document not just what you're building, but why you're building it, what constraints shape the solution, and what success looks like from multiple perspectives. This extends beyond the requirements to encompass business context, technical constraints, and stakeholder expectations.</p>
</li>
<li><p><strong>Processing</strong>: Transform raw information into decision-ready insights through specialized analysis. Instead of a generic "understand the codebase," you get targeted investigations: "identify authentication patterns that will be affected by this change" or "map business rules that constrain this feature."</p>
</li>
<li><p><strong>Transferring</strong>: Ensure context flows between phases without degradation. The person implementing code has access to the strategic reasoning behind architectural decisions. The person debugging production issues understands the business constraints that shaped the original feature.</p>
</li>
<li><p><strong>Specializing</strong>: Match different types of contextual reasoning to different kinds of problems. Strategic analysis requires different thinking patterns than implementation planning, which differs from production debugging. Each specialized approach amplifies AI assistance in its specific domain.</p>
</li>
</ul>
<p>The breakthrough isn't just organizing context better. It's recognizing that AI excels at this kind of systematic context management when properly guided.</p>
<p>Contextual Intelligence doesn't replace existing methodologies, such as Domain-Driven Design, Event Storming, or Architecture Decision Records. Instead, it amplifies them. Your DDD sessions generate richer context when guided by strategic analysis. Your ADRs become more comprehensive when informed by systematic discovery. Your architectural patterns integrate more smoothly when implementation follows contextual planning.</p>
<p>The key insight is that AI excels at this kind of cross-methodology context synthesis when it is adequately specialized for each type of reasoning.</p>
<h2 id="heading-beyond-tools-a-way-of-thinking"><strong>Beyond Tools: A Way of Thinking</strong></h2>
<p>Experienced engineers already have a structured approach to the problems. They know what information matters when discussing business stakeholders versus troubleshooting production issues. They understand which parts of the codebase will be affected by a new feature. They can estimate refactoring efforts because they grasp the full scope of changes.</p>
<p>The transformation occurs when these thinking patterns become well-known and repeatable:</p>
<ul>
<li><p><strong>Explicit</strong>: Everyone knows what type of thinking is needed when</p>
</li>
<li><p><strong>Systematic</strong>: Consistent approaches rather than ad hoc reactions</p>
</li>
<li><p><strong>Transferable</strong>: New team members learn proven patterns, not personal preferences</p>
</li>
<li><p><strong>Augmentable</strong>: AI assistance amplifies good practices instead of accelerating bad ones</p>
</li>
<li><p><strong>Iterative</strong>: Teams can refine and enhance their approaches over time</p>
</li>
</ul>
<h2 id="heading-implementing-contextual-intelligence-the-persona-approach"><strong>Implementing Contextual Intelligence: The Persona Approach</strong></h2>
<p>Traditional development is prone to inevitable information loss. The business analyst who understood edge cases isn't the person writing code. The architect who made trade-off decisions isn't debugging production issues months later. The developer who built the feature is no longer maintaining it.</p>
<p>Requirements documents get stale. Architecture decisions get forgotten. Implementation details exist only in someone's head until they leave the company.</p>
<p>Contextual Intelligence flips this pattern. Each development phase enriches the context for the next phase. Instead of progressive information loss, you get progressive context accumulation.</p>
<p>After months of developing these specialized agents, I discovered that <a target="_blank" href="https://bsky.app/profile/humanwhocodes.com">Nicholas Zakas</a> had published a similar concept in his article <a target="_blank" href="https://humanwhocodes.com/blog/2025/06/persona-based-approach-ai-assisted-programming/">"A persona-based approach to AI-assisted programming"</a>. Interestingly, I had been calling my agents by functional names, "the discoverer," "the planner," "the executor", until reading his work made me realize I was essentially creating personas for different types of reasoning. This insight led me to fully embrace the persona concept.</p>
<p>Where Zakas focuses on leveraging the strengths of different AI models for distinct roles, my approach emphasizes building and transferring persistent understanding between development phases. Both approaches share the core insight that specialized AI agents outperform generic assistants. However, they solve different problems.</p>
<p>The persona approach I developed identifies six distinct agents, each designed to excel at specific types of contextual reasoning:</p>
<h3 id="heading-the-farseer-strategic-contextual-intelligence"><strong>🔮 The Farseer</strong> - Strategic Contextual Intelligence</h3>
<p><em>Transforms "this sounds good" into a systematic feasibility assessment</em></p>
<p>Before Sarah started coding, she analyzed whether the authentication change aligned with the product roadmap, identified potential conflicts with upcoming features, and assessed technical risks. The Farseer persona transforms "this sounds good" into a systematic feasibility assessment by asking: What business constraints will this change reveal? What technical debt will it expose? Which stakeholders need to approve this direction?</p>
<h3 id="heading-the-spiritwalker-archaeological-contextual-intelligence"><strong>👻 The Spiritwalker</strong> - Archaeological Contextual Intelligence</h3>
<p><em>Bridges knowledge gaps between system experts and newcomers</em></p>
<p>The Spiritwalker helped Sarah understand why the authentication system was built the way it was, what constraints had shaped its design, and which assumptions still held true. This persona specializes in legacy system discovery, excavating the historical context buried in code comments, Git history, and institutional memory to prevent "simple changes" from becoming unexpected refactorings.</p>
<h3 id="heading-the-kodo-architectural-contextual-intelligence"><strong>🦏 The Kodo</strong> - Architectural Contextual Intelligence</h3>
<p><em>Creates implementable designs before coding begins</em></p>
<p>Instead of figuring out the architecture while coding, the Kodo persona maps out technical approaches upfront. Sarah knew exactly how his change would integrate with existing patterns, what new patterns might be required, and, most importantly, how to minimize complexity rather than inadvertently increase it.</p>
<h3 id="heading-the-chieftain-organizational-contextual-intelligence"><strong>⚔️ The Chieftain</strong> - Organizational Contextual Intelligence</h3>
<p><em>Breaks overwhelming requirements into manageable work</em></p>
<p>Complex features often conceal multiple user stories disguised as a single requirement. The Chieftain persona systematically decomposes features by user impact, technical dependency, and risk profile, transforming "add social login" into separate stories for OAuth integration, user migration, privacy compliance, and error handling, each with clear acceptance criteria.</p>
<h3 id="heading-the-grunt-implementation-contextual-intelligence"><strong>🗡️ The Grunt</strong> - Implementation Contextual Intelligence</h3>
<p><em>Delivers consistent quality through systematic patterns</em></p>
<p>The Grunt persona ensures implementation follows established team patterns and quality standards. Far from mindless execution, this persona applies hard-won implementation wisdom: which error handling patterns work in this codebase, how to write maintainable tests, and when to refactor versus working around existing code.</p>
<h3 id="heading-the-witchdoctor-diagnostic-contextual-intelligence"><strong>🧙 The Witchdoctor</strong> - Diagnostic Contextual Intelligence</h3>
<p><em>Provides early warning systems for project health</em></p>
<p>Projects fail gradually, then suddenly. The Witchdoctor persona monitors system health through multiple lenses: Are implementation decisions aligning with architectural plans? Is technical debt accumulating faster than expected? Are stakeholder expectations drifting from actual capabilities? Early warning systems prevent minor problems from escalating into project-crippling crises.</p>
<h2 id="heading-making-it-real-building-the-system"><strong>Making It Real: Building the System</strong></h2>
<p>The evolution from those initial throw-away Claude Code sessions to systematic contextual Intelligence didn't happen overnight. Once I recognized the pattern, I had to figure out how to make it work reliably.</p>
<p>The key insight was that each persona needed to both consume context from previous phases and generate enriched context for the next. This isn't just about having six different AI assistants. It's about creating <strong>a context pipeline where understanding accumulates and transfers</strong>.</p>
<p>Here's how it works in practice:</p>
<p><strong>The Farseer</strong> analyzes a business request and saves strategic context, including market constraints, business priorities, and technical feasibility boundaries. This context becomes the foundation that <strong>The Spiritwalker</strong> uses to determine which parts of the existing system are relevant to this specific change.</p>
<p><strong>The Spiritwalker's</strong> archaeological findings inform <strong>The Kodo</strong>, which now possesses both business context and system understanding to create architectural plans that respect existing patterns while achieving business goals.</p>
<p><strong>The Kodo's</strong> technical design guides <strong>The Chieftain</strong> in breaking down work into stories that reflect both the technical realities and business priorities. Each story carries forward the accumulated understanding.</p>
<p><strong>The Grunt</strong> implements these stories with full context about why each decision was made, what constraints matter, and how the code fits into the larger system vision.</p>
<p>Finally, <strong>The Witchdoctor</strong> monitors progress with a complete understanding of what success looks like from business, technical, and organizational perspectives.</p>
<p><strong>At each handoff, context isn't lost. On the contrary, it's enriched. By the time you're implementing, you're not coding from requirements. You're coding from understanding.</strong></p>
<p>This is what transforms vibe coding into systematic development: persistent context that flows between specialized reasoning capabilities, each adding its own layer of insight while preserving everything that came before.</p>
<h2 id="heading-a-quick-note-on-naming-yes-i-love-the-frozen-throne"><strong>A Quick Note on Naming (Yes, I love The Frozen Throne)</strong></h2>
<p>Before you think I've lost my mind with these agent names, let me explain: they're all borrowed from "<a target="_blank" href="https://warcraft3.blizzard.com/en-us/">Warcraft 3: The Frozen Throne</a>" orc units, and it turns out that Blizzard's game designers accidentally created perfect metaphors for software development workflows. The Farseer has "Farsight" to reveal distant areas of the map, just like strategic planning reveals business landscapes. The Spiritwalker exists in ethereal form to bridge different worlds, much like bridging knowledge gaps in legacy systems. The Kodo Beast devours enemies whole and uses war drums to boost nearby allies, which is basically what good architecture does to complexity. The Grunt is the reliable warrior who actually gets stuff done. And the Witchdoctor places sentry wards across the battlefield for early warning systems, exactly like project health monitoring.</p>
<p>The more I thought about it, the more I realized these fantasy units capture the essence of different types of contextual reasoning better than any corporate buzzwords ever could. Plus, "<strong>Farseer Analysis</strong>" sounds way more interesting than "Strategic Requirements Assessment."</p>
<h2 id="heading-the-path-forward"><strong>The Path Forward</strong></h2>
<p>Marcus is still debugging his "quick fix." Sarah shipped her change and moved on to implementing the next feature with confidence.</p>
<p>The transformation isn't about having a better AI tool. It's about <strong>evolving your relationship with AI from a reactive assistant to a collaborative partner in building understanding</strong>.</p>
<p>You don't need to implement all six personas to start reaping the benefits. Choose the area where you struggle most: strategic alignment, system understanding, architectural planning, work breakdown, implementation quality, or project monitoring. Build contextual Intelligence in that one area, and watch how it transforms not just your code, but your entire development process.</p>
<p>Over the next six posts, I'll share the specific configurations, prompts, and workflows that make each persona effective. However, <strong>the real journey begins with recognizing that context isn't magic. It's a discipline</strong>. And disciplines can be learned, systematized, and shared.</p>
<p>Ready to evolve beyond vibe coding?</p>
<hr />
<p><strong>A Note on AI Tool Dependencies</strong>: This approach relies on the availability and effectiveness of AI assistance. The personas, prompts, and workflows I'll share are built for current AI capabilities. As these tools evolve, the specific implementations will need to adapt, but the underlying principles of structured context management remain tool-agnostic.</p>
<hr />
<p><em>Next up:</em> <strong><em>Part 1: The Farseer</em></strong>*, From gut feelings to systematic feasibility assessment, plus the complete Claude agent implementation for strategic analysis.*</p>
]]></content:encoded></item><item><title><![CDATA[Handling HTTP timeouts in Fastify]]></title><description><![CDATA[The hardest part of my job is thinking like a malicious user... or like an AI in its early stages of writing HTTP servers!This exercise is not easy because I love my clients' HTTP requests, and I'm sure they love my server!But that is not always the ...]]></description><link>https://backend.cafe/handling-http-timeouts-in-fastify</link><guid isPermaLink="true">https://backend.cafe/handling-http-timeouts-in-fastify</guid><category><![CDATA[fastify]]></category><category><![CDATA[timeout]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Fri, 30 May 2025 08:10:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/e5sR26bNdPw/upload/2843ccc6d9cf2c998f24acbc2038e984.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The hardest part of my job is thinking like a malicious user... or like an AI in its early stages of writing HTTP servers!<br />This exercise is not easy because I love my clients' HTTP requests, and I'm sure they love my server!<br />But that is not always the case... for this reason, we must be prepared to handle unexpected situations.</p>
<p>In this article, we will explore all the options available in Fastify to handle timeouts!<br />A poorly implemented client or a malicious user could kill your server if we are not careful!</p>
<h2 id="heading-how-many-timeouts-do-i-need">How many timeouts do I need?</h2>
<p>Before listing all the timeout options available in Fastify, let's take a moment to understand the HTTP request lifecycle and identify where we need to set timeouts.</p>
<p>In the following diagram, we can see a simplified HTTP request lifecycle that includes the most important steps:</p>
<ol>
<li><p>Establishing a TCP connection to create a socket.</p>
</li>
<li><p>TLS handshake (if using HTTPS).</p>
</li>
<li><p>At this stage, the socket is open, and the server is waiting for the client to send the HTTP request as the protocol requires.</p>
</li>
<li><p>The server receives the request and processes it.</p>
</li>
<li><p>The server sends the response to the client.</p>
</li>
<li><p>The server either closes the connection or keeps it alive for future requests using the same socket.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748354190553/9bedc985-9c5f-4f20-9a46-60f663e72a9c.png" alt class="image--center mx-auto" /></p>
<p>In the diagram, the timeouts are represented with a clock icon ⏰.<br />So, let's review them all <em>(note that the default values are Fastify defaults, not the Node.js ones)</em>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Timeout setting</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>headersTimeout</code></td><td>Specifies the number of milliseconds to wait for the complete HTTP headers to be received. If the headers are not received within this period, the connection is terminated. <a target="_blank" href="https://nodejs.org/api/http.html#serverheaderstimeout">Default: <code>no-limit</code></a></td></tr>
<tr>
<td><code>requestTimeout</code></td><td>Defines the maximum duration, in milliseconds, to wait for the entire request from the client. If the request body isn't fully received within this time frame, the connection is closed. <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Server/#requesttimeout">Default: <code>no-limit</code></a>.</td></tr>
<tr>
<td><code>connectionTimeout</code> (aka <code>timeout</code>)</td><td>Sets the socket inactivity timeout in milliseconds. If there's no activity on the socket for the specified duration, the server closes the connection. <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Server/#connectiontimeout">Default: <code>no-limit</code></a></td></tr>
<tr>
<td><code>keepAliveTimeout</code></td><td>Determines the time, in milliseconds, to wait for additional requests on a persistent connection (keep-alive). If no new request is received within this period, the server closes the connection. <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Server/#keepalivetimeout">Default: 72 seconds</a></td></tr>
<tr>
<td><code>connectionsCheckingInterval</code></td><td>Sets the interval value, in milliseconds, to check for request and headers timeout in incomplete requests. <a target="_blank" href="https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener">Default: 30,000</a></td></tr>
</tbody>
</table>
</div><p>Wow, that's a lot of timeouts! But why do we need all of them?</p>
<p>All these timeouts are crucial to ensuring that the server can handle incoming requests efficiently and prevent resource exhaustion. Otherwise, a malicious user could send a request with a large payload and keep the connection open indefinitely, causing the server to run out of memory or <a target="_blank" href="https://en.wikipedia.org/wiki/File_descriptor">file descriptors</a>!</p>
<p>For example, <code>headersTimeout</code> ensures that the server doesn't wait indefinitely for the client to send the headers.<br />This option helps prevent the <a target="_blank" href="https://www.cloudflare.com/it-it/learning/ddos/ddos-attack-tools/slowloris/">Slowloris attack</a>, where an attacker sends headers very slowly to keep the connection open and exhaust server resources.</p>
<p>Now that we understand timeouts and their importance, let's see how to set them up in Fastify.</p>
<h2 id="heading-setting-up-timeouts-in-fastify">Setting up timeouts in Fastify</h2>
<p>To configure timeouts in Fastify, we must pass the options when creating the Fastify instance.<br />Here's a <strong>TL;DR</strong> example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> fastify <span class="hljs-keyword">from</span> <span class="hljs-string">"fastify"</span>;

<span class="hljs-keyword">const</span> app = fastify({
  <span class="hljs-comment">// 2 minutes: a reasonable timeout for processing requests, balancing performance and user experience</span>
  <span class="hljs-attr">connectionTimeout</span>: <span class="hljs-number">120</span>_000,

  <span class="hljs-comment">// 1 minute: suitable for most payloads, including moderate file uploads</span>
  <span class="hljs-attr">requestTimeout</span>: <span class="hljs-number">60</span>_000,

  <span class="hljs-comment">// 10 seconds: ensures efficient resource usage for idle connections</span>
  <span class="hljs-attr">keepAliveTimeout</span>: <span class="hljs-number">10</span>_000,

  <span class="hljs-attr">http</span>: {
    <span class="hljs-comment">// 15 seconds: prevents slow clients from holding connections too long</span>
    <span class="hljs-attr">headersTimeout</span>: <span class="hljs-number">15</span>_000,
  },
});
</code></pre>
<p>These are general recommendations, and the actual values may vary based on your application and use case.<br />Be sure to think about the following:</p>
<ul>
<li><p><strong>Protect the server</strong>: Prevent resource exhaustion by limiting how long connections can remain open or idle.</p>
</li>
<li><p><strong>Enhance client experience</strong>: Provide sufficient time for legitimate requests, even under less-than-ideal network conditions.</p>
</li>
<li><p><strong>Balance performance and security</strong>: Avoid excessively long timeouts that could lead to inefficiency or abuse, while ensuring the server remains responsive.</p>
</li>
</ul>
<p>Now, let's test the timeouts!<br />I created a simple Fastify v5 server and a client to test the timeouts.<br />For the sake of simplicity, the code can be found on <a target="_blank" href="https://github.com/Eomm/blog-posts/tree/HEAD/bonus/timeouts">GitHub</a> and is not included in this article.</p>
<h2 id="heading-testing-timeouts">Testing timeouts</h2>
<p>If you have downloaded and installed the code from the <a target="_blank" href="https://github.com/Eomm/blog-posts/tree/HEAD/bonus/timeouts">GitHub repository</a>, we can run the server with Node.js v20+ and the following command, and we can set the timeouts via optional arguments:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># To start the server</span>
node server.js --connectionTimeout 10000 \
  --requestTimeout 20000 \
  --keepAliveTimeout 5000 \
  --headersTimeout 30000 \
  --handlerTimeout 5000 \
  --connectionsCheckingInterval 10000
</code></pre>
<p>Then, we can run the client with the following command, where we can set the payload and header rate:</p>
<ul>
<li><p><code>--headerRate</code>: we send a header every <code>headerRate</code> milliseconds</p>
</li>
<li><p><code>--payloadRate</code>: we send a byte every <code>payloadRate</code> milliseconds</p>
</li>
<li><p><code>--keepAlive</code>: if set, the client will keep the connection alive but will not send any new request</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-comment"># To start the client</span>
node low-level-client.js --headerRate 1000 \
  --payloadRate 1000
</code></pre>
<p>Cool! Now we can test the timeouts by running a simple command!</p>
<h3 id="heading-test-the-headerstimeout">Test the <code>headersTimeout</code></h3>
<p>Straight to the point: let's test the <code>headersTimeout</code> by sending a request with a very slow header rate.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Set the header timeout to half a second</span>
node server.js --headersTimeout 500

<span class="hljs-comment"># Set the header rate to 1 second</span>
node low-level-client.js --headerRate 1000
</code></pre>
<p>The expected output is a timeout error, because the server will wait for 500 milliseconds to collect all the headers, but the client will send one header every second.</p>
<p>But, if we run those commands, we will see that the server will not throw any error 😱!</p>
<pre><code class="lang-bash">➜ node low-level-client.js --headerRate 1000
Connected to server
Sent Header: Host: localhost
Sent Header: User-Agent: Slow-Client
Sent Header: Accept: application/json
Sent Header: Content-Type: application/json
Sent Header: Content-Length: 83
Sent Header: Connection: keep-alive
Finished sending headers. Now sending body...
{<span class="hljs-string">"message"</span>:<span class="hljs-string">"Hello, this is a slow request!"</span>,<span class="hljs-string">"timestamp"</span>:<span class="hljs-string">"2025-03-30T14:02:08.156Z"</span>}

Received response: HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 17
Date: Sun, 30 Mar 2025 14:02:22 GMT
Connection: keep-alive
Keep-Alive: timeout=72

{<span class="hljs-string">"hello"</span>:<span class="hljs-string">"world"</span>}
Disconnected from server after 14338 ms
</code></pre>
<p>The reason is that Node.js does not check the timeouts so frequently. Actually, the default <code>connectionsCheckingInterval</code> configuration is set to 30 seconds, so the server will check the timeouts every 30 seconds.</p>
<p>This makes sense, because checking all the sockets every few milliseconds would be a waste of CPU resources. As always, we developers need to find a balance between performance and security.</p>
<p>So, for the sake of this article, let's set the <code>connectionsCheckingInterval</code> to 100 milliseconds to appreciate the timeouts in real time, or if you would like to see a real Slowloris attack in action, set the <code>headerRate</code> to 60 seconds!</p>
<pre><code class="lang-bash">node server.js --headersTimeout 500 \
  --connectionsCheckingInterval 1000

node low-level-client.js --headerRate 2000
</code></pre>
<p>Cool! Now we can see the timeout in action:</p>
<pre><code class="lang-bash">Connected to server
Sent Header: Host: localhost
Received response: HTTP/1.1 408 Request Timeout
Content-Length: 71
Content-Type: application/json

{<span class="hljs-string">"error"</span>:<span class="hljs-string">"Request Timeout"</span>,<span class="hljs-string">"message"</span>:<span class="hljs-string">"Client Timeout"</span>,<span class="hljs-string">"statusCode"</span>:408}
Disconnected from server after 549 ms
</code></pre>
<h3 id="heading-test-result">Test result</h3>
<p>Now, we can run a lot of tests with different timeouts and payloads to see how the server behaves. Here is a summary of the tests I ran <em>(considering the</em> <code>connectionsCheckingInterval</code> set to 100 milliseconds):</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Test</td><td>Client args</td><td>Server args</td><td>Result</td></tr>
</thead>
<tbody>
<tr>
<td>Slow Headers</td><td><code>--headerRate 1000</code></td><td><code>--headersTimeout 500</code></td><td>Timeout error: because the server will wait for 500ms to receive the headers, but the client will send one header every second.</td></tr>
<tr>
<td>Slow Payload</td><td><code>--payloadRate 1000</code></td><td><code>--requestTimeout 5000</code></td><td>Timeout error: because the server will wait for five seconds to receive the payload, but the client will send one byte every second.</td></tr>
<tr>
<td>Keep-Alive Connection</td><td><code>--payloadRate 1 --keepAlive</code></td><td><code>--keepAliveTimeout 4000</code></td><td>After completing the request, the socker will be ended by the server after four seconds.</td></tr>
<tr>
<td>Slow Server handler</td><td><code>--headerRate 1</code></td><td><code>--connectionTimeout 10000 --handlerTimeout 60000</code></td><td>Timeout error: because the server will wait for ten seconds to process the whole request, but the server handler will take 60 seconds to process the request.</td></tr>
</tbody>
</table>
</div><p>Now we can see that the server is able to handle the timeouts correctly and, the client is able to receive the timeout errors.</p>
<p>Use those scripts to test the timeouts in your own environment and see how they behave.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we explored the different timeouts available in Fastify and how to configure them.<br />We also tested them with a simple Fastify server and a custom socket.<br />We learned that timeouts are essential for preventing resource exhaustion and ensuring that<br />the server efficiently handles incoming requests.</p>
<p>Keep in mind that every application is different, but we should never forget to set timeouts!</p>
<p>If you enjoyed this article, you might like <a target="_blank" href="https://backend.cafe/the-fastify-book-is-out"><em>"Accelerating Server-Side Development with Fastify"</em></a>.<br />Comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">X/Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Managing Request-Scoped Data in Fastify]]></title><description><![CDATA[I was looking at the Fastify plugin list
and found myself asking, "What is this @fastify/request-context plugin?"So, I checked it out, and here I am, writing a post about it to share what it is and how it can be useful for you!
What is AsyncLocalStor...]]></description><link>https://backend.cafe/managing-request-scoped-data-in-fastify</link><guid isPermaLink="true">https://backend.cafe/managing-request-scoped-data-in-fastify</guid><category><![CDATA[fastify]]></category><category><![CDATA[isolation]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Wed, 02 Apr 2025 06:44:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Yx1tUkOBtk8/upload/641d12088c9cc595307e11690bbd5e00.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was looking at the <a target="_blank" href="https://eomm.notion.site/7a064537ee794af698684df68e215b54?v=4034009f43bd4d599a31701c4246d9fa&amp;pvs=4">Fastify plugin list</a>
and found myself asking, <em>"What is this <a target="_blank" href="https://github.com/fastify/fastify-request-context"><code>@fastify/request-context</code> plugin</a>?"</em><br />So, I checked it out, and here I am, writing a post about it to share what it is and how it can be useful for you!</p>
<h2 id="heading-what-is-asynclocalstorage">What is AsyncLocalStorage?</h2>
<p>Managing state across asynchronous operations has always been a challenge in Node.js.<br />Traditional methods like passing context objects through function parameters or using global variables are the easiest way to share data across functions. However, they can quickly become unmanageable, especially in large applications with deeply nested asynchronous calls, making the code difficult to test.</p>
<p>This is where <a target="_blank" href="https://nodejs.org/api/async_context.html#class-asynclocalstorage"><strong>AsyncLocalStorage</strong></a>,
a core module introduced in Node.js 13, comes in handy.<br />It provides a way to store and retrieve data that persists through an asynchronous execution context.
Unlike global variables, which are shared across all requests, <code>AsyncLocalStorage</code> allows developers
to maintain <strong>request-scoped data</strong>, meaning each incoming request gets <strong>its own isolated storage</strong>.  </p>
<p>This feature seems to overlap with <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Decorators/">Fastify's Decorators</a>, but it's not the same. Let's see why!</p>
<h3 id="heading-how-it-works">How It Works</h3>
<p>The basic idea behind <code>AsyncLocalStorage</code> is that it creates an execution context that is preserved
throughout asynchronous operations, even across <code>setTimeout</code> or database queries.  </p>
<p>Here’s a simple example with comments:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { AsyncLocalStorage } <span class="hljs-keyword">from</span> <span class="hljs-string">"async_hooks"</span>;

<span class="hljs-comment">// Create a new instance of AsyncLocalStorage that will be unique per application</span>
<span class="hljs-keyword">const</span> appAsyncLocalStorage = <span class="hljs-keyword">new</span> AsyncLocalStorage();

<span class="hljs-comment">// Simulate an incoming request every 2 seconds</span>
<span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// Generate a random request ID that will be unique per request</span>
  <span class="hljs-keyword">const</span> requestId = <span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">36</span>).substring(<span class="hljs-number">7</span>);

  <span class="hljs-comment">// Run the `reqHandler` function in the AsyncLocalStorage context</span>
  <span class="hljs-comment">// This creates the context and binds the `store` object to it</span>
  <span class="hljs-keyword">const</span> store = { requestId };
  appAsyncLocalStorage.run(store, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reqHandler</span>(<span class="hljs-params"></span>) </span>{
    logWithRequestId(<span class="hljs-string">"Processing request..."</span>);
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> logWithRequestId(<span class="hljs-string">"Finished processing."</span>), <span class="hljs-number">3</span>_000);
  });
}, <span class="hljs-number">2</span>_000);

<span class="hljs-comment">// Main business logic function</span>
<span class="hljs-comment">// Through `appAsyncLocalStorage.getStore()`, we can access the `store` object</span>
<span class="hljs-comment">// that was bound to the AsyncLocalStorage context in `reqHandler`</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logWithRequestId</span>(<span class="hljs-params">message</span>) </span>{
  <span class="hljs-keyword">const</span> store = appAsyncLocalStorage.getStore();
  <span class="hljs-keyword">const</span> requestId = store?.requestId || <span class="hljs-string">"unknown"</span>;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[Request <span class="hljs-subst">${requestId}</span>]: <span class="hljs-subst">${message}</span>`</span>);
}
</code></pre>
<p>The above code snippet provides the <code>requestId</code> to the <code>logWithRequestId</code> function without passing it as a parameter!<br />It still requires access to the <code>appAsyncLocalStorage</code> instance to retrieve the <code>store</code> object,
but with a single variable, we can access everything we need throughout the request context.</p>
<h3 id="heading-why-is-this-important">Why Is This Important?</h3>
<p>Without <code>AsyncLocalStorage</code>, you would need to manually pass the <code>requestId</code> to every function that requires it,
which can be cumbersome and error-prone.<br />With <code>AsyncLocalStorage</code>, the context is automatically preserved throughout the request lifecycle,
making it much easier to track request-specific data.  </p>
<p>Think about all the times you've had to pass a <code>logging</code> or <code>config</code> object to every function.<br />Or when you manually tracked the start and end of a request.<br />Or even when you implemented a tracing system to follow requests through multiple services.  </p>
<p>With <code>AsyncLocalStorage</code>, you can forget about that spaghetti code and focus on the request context's store!</p>
<h2 id="heading-how-to-use-the-fastifyrequest-context-plugin">How to Use the <code>@fastify/request-context</code> Plugin</h2>
<p>Fastify already solves multiple problems with its decorators:</p>
<ul>
<li>It provides logging through the <code>request.log</code> object.</li>
<li>It provides configuration through the <code>fastify.config</code> object, thanks to the <a target="_blank" href="https://github.com/fastify/fastify-env"><code>@fastify/env</code> plugin</a>.</li>
<li>It supports <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Hooks/#diagnostics-channel-hooks">Diagnostic Channels</a> to track the request lifecycle.</li>
</ul>
<p>The <a target="_blank" href="https://github.com/fastify/fastify-request-context"><code>@fastify/request-context</code></a> plugin takes things
further by offering a structured way to manage request-scoped data without the hassle of manual context management.</p>
<h3 id="heading-quick-start">Quick Start</h3>
<p>After installing the plugin, you can register it in your Fastify application.<br />Let’s see a real-world example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Fastify <span class="hljs-keyword">from</span> <span class="hljs-string">"fastify"</span>;
<span class="hljs-keyword">import</span> fastifyRequestContext <span class="hljs-keyword">from</span> <span class="hljs-string">"@fastify/request-context"</span>;

<span class="hljs-keyword">const</span> app = Fastify({ <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span> });

app.register(fastifyRequestContext, {
  defaultStoreValues() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">logicStep</span>: [],
    };
  },
});

app.get(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">longHandler</span>(<span class="hljs-params">req, reply</span>) </span>{
  <span class="hljs-keyword">const</span> debugBusiness = req.requestContext.get(<span class="hljs-string">"logicStep"</span>);

  <span class="hljs-comment">// Simulate some business logic</span>
  debugBusiness.push(<span class="hljs-string">"Called external service 1"</span>);
  <span class="hljs-comment">// Do something...</span>
  debugBusiness.push(<span class="hljs-string">"Processed external service 2"</span>);

  <span class="hljs-comment">// Simulate an error</span>
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Something went wrong 😱"</span>);
});

app.setErrorHandler(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, request, reply</span>) </span>{
  <span class="hljs-keyword">const</span> debugBusiness = request.requestContext.get(<span class="hljs-string">"logicStep"</span>);

  <span class="hljs-built_in">this</span>.log.error({ err, debugBusiness }, <span class="hljs-string">"An error occurred"</span>);
  reply.status(<span class="hljs-number">500</span>).send(<span class="hljs-string">"Internal Server Error"</span>);
});

app.inject(<span class="hljs-string">"/"</span>);
</code></pre>
<p>In this example, we have a Fastify application with a single route that simulates a complex business logic flow.<br />Thanks to the <code>@fastify/request-context</code> plugin, we can store the <code>logicStep</code> array in the request context and access it
in every part of the application by accessing the <code>request</code> object.</p>
<p>The real power of this plugin is that you can access the <code>logicStep</code> array in
the error handler <strong>without passing it through function parameters</strong>.<br />This allows you to push trace information across the entire application and log it when an error occurs,
helping you understand what happened before the failure.</p>
<p>Manually passing request-specific data through function arguments makes the code messy and difficult to maintain.<br />With this plugin, you can access context-specific data anywhere in the request lifecycle without modifying function signatures.</p>
<h2 id="heading-fastifyrequest-context-vs-decorators"><code>@fastify/request-context</code> vs Decorators</h2>
<p>Why use the <code>@fastify/request-context</code> plugin and <code>AsyncLocalStorage</code> instead of Fastify's Request and Reply decorators?<br />Aren't they achieving the same goal?</p>
<p><em>Not exactly.</em></p>
<p>To understand the difference, let’s briefly look at how Fastify decorators work.<br />Fastify decorators mutate the Request and Reply prototypes, adding new properties to them.<br />Every time an HTTP request is received, Fastify creates new Request and Reply objects using these modified prototypes,
automatically making decorators available.</p>
<p>Sounds great, right? But there’s a catch!</p>
<p>If you add <strong>reference-type objects</strong> (arrays or JSON objects) to the Request or Reply object,
they will be shared across all requests.<br />If one request modifies them, it will affect all other requests!<br />This can lead to unexpected behavior and hard-to-track bugs.  </p>
<p>Due to this, the Fastify team <strong>discouraged</strong> using reference-type objects in decorators
starting from <a target="_blank" href="https://fastify.dev/docs/latest/Guides/Migration-Guide-V5/#removed-support-from-reference-types-in-decorators">v5</a>.</p>
<p>You can still use decorators for reference-type objects, but it becomes more complex and error-prone.<br />Here’s an example requiring <strong>two decorators</strong> and a <strong>lazy-loading approach</strong>:</p>
<pre><code class="lang-javascript">app.decorateRequest(<span class="hljs-string">'logicStepValue'</span>, <span class="hljs-literal">null</span>)
app.decorateRequest(<span class="hljs-string">'logicStep'</span>, {
  getter () {
    <span class="hljs-built_in">this</span>.logicStepValue ??= [];
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.logicStepValue;
  },
})
</code></pre>
<p>This is where the <code>@fastify/request-context</code> plugin shines.<br />It provides a <strong>structured</strong>, <strong>safe</strong>, and <strong>isolated</strong> way to manage request-specific
data <strong>without the risk of data leakage across requests</strong>!</p>
<h2 id="heading-summary">Summary</h2>
<p>The <code>@fastify/request-context</code> plugin offers a structured, efficient way to manage request-scoped data in Fastify applications.<br />It simplifies code, improves maintainability, enhances logging, and makes debugging significantly easier.  </p>
<p>By leveraging this plugin, you ensure that request-specific data
remains <strong>consistent</strong> and <strong>accessible</strong> throughout the request lifecycle <strong>without unnecessary complexity</strong>.</p>
<p>If you enjoyed this article, you might like <a target="_blank" href="https://backend.cafe/the-fastify-book-is-out"><em>"Accelerating Server-Side Development with Fastify"</em></a>.<br />Comment, share, and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">X/Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Announcing the Fastify Mini Course]]></title><description><![CDATA[Dear Backend.cafe readers, I have something special for you!
If you've ever wanted to master Fastify, the lightning-fast Node.js framework, you're in for a treat.
Today, I’m launching Fastify Mini Course—a free resource designed to help you build hig...]]></description><link>https://backend.cafe/announcing-the-fastify-mini-course</link><guid isPermaLink="true">https://backend.cafe/announcing-the-fastify-mini-course</guid><category><![CDATA[fastify]]></category><category><![CDATA[course]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sat, 01 Mar 2025 09:02:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ymf4_9Y9S_A/upload/37baada78e1522ca14ba977f5f6b5d92.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Dear Backend.cafe readers, I have something special for you!
If you've ever wanted to master <strong>Fastify</strong>, the lightning-fast Node.js framework, you're in for a treat.
Today, I’m launching <strong>Fastify Mini Course</strong>—a free resource designed to help you build high-performance APIs efficiently.</p>
<h2 id="heading-why-fastify">🚀 Why Fastify?</h2>
<p>If you're a backend developer, you've probably worked with the <code>http</code> module. But have you considered switching to Fastify?
Here’s why you should:</p>
<ul>
<li><strong>Blazing Fast</strong>: Up to <strong>4x faster</strong> than the most popular Node.js frameworks.</li>
<li><strong>Low Overhead</strong>: Optimized for performance and scalability.</li>
<li><strong>Schema-Based Validation</strong>: First-class TypeScript support and JSON schema validation.</li>
<li><strong>Modern Features</strong>: Built-in async/await, hooks, decorators, and powerful plugin system.</li>
</ul>
<p>Whether you're an <code>http</code> pro or new to backend development, this mini-course will help you level up your skills!</p>
<h2 id="heading-whats-inside-the-mini-course">📖 What’s Inside the Mini Course?</h2>
<p>This course is <strong>bite-sized</strong> but <strong>powerful</strong>. You’ll learn:</p>
<ul>
<li>✅ How to set up a Fastify project from scratch.</li>
<li>✅ Routing, request validation, and response serialization.</li>
<li>✅ Working with middleware, hooks, and decorators.</li>
<li>✅ Optimizing Fastify for production-ready performance.</li>
<li>✅ Real-world best practices.</li>
</ul>
<p>The course is designed for <strong>maximum efficiency</strong>, so you can complete it in just a few hours and walk away with practical Fastify skills.
You will get one email per day, each containing a short lesson and a hands-on exercise.</p>
<h2 id="heading-who-is-this-for">🎯 Who Is This For?</h2>
<ul>
<li>Developers who want to learn Fastify from scratch.</li>
<li>Anyone interested in improving API performance and scalability.</li>
<li>Engineers looking for a quick and structured way to learn Fastify.</li>
</ul>
<h2 id="heading-how-to-get-access">📅 How to Get Access</h2>
<p>It is available <strong>publicly</strong> at <a target="_blank" href="https://fastifyminicourse.com/">FastifyMiniCourse.com</a>.</p>
<h2 id="heading-get-ready-to-build-faster-apis">🚀 Get Ready to Build Faster APIs!</h2>
<p>I can’t wait for you to try it out! If you have any questions or feedback, let’s discuss them in the comments.</p>
<p>Happy coding! 🎯</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://x.com/ManuEomm">X</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Master Node.js with the 5th Edition Cookbook]]></title><description><![CDATA[We are excited to announce that the Node.js Cookbook, 5th Edition is set to be released on November 15th, 2024!
This latest edition, authored by Bethany Griggs, is the ultimate guide for developers who want to master the Node.js v22 API and build sca...]]></description><link>https://backend.cafe/master-nodejs-with-the-5th-edition-cookbook</link><guid isPermaLink="true">https://backend.cafe/master-nodejs-with-the-5th-edition-cookbook</guid><category><![CDATA[Node.js]]></category><category><![CDATA[books]]></category><category><![CDATA[fastify]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Wed, 23 Oct 2024 07:00:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FxtIWX8Q0J4/upload/708d1a80ff8e708ad41094f32f7b121e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We are excited to announce that the <em>Node.js Cookbook, 5th Edition</em> is set to be released on <strong>November 15th, 2024</strong>!</p>
<p>This latest edition, authored by <a target="_blank" href="https://github.com/BethGriggs"><strong>Bethany Griggs</strong></a>, is the ultimate guide for developers who want to master the Node.js v22 API and build scalable, high-performance applications.</p>
<p>The <em>Node.js Cookbook</em> is a trusted resource for new and experienced developers alike, packed with practical recipes and solutions to common Node.js challenges. Whether you're just starting your Node.js journey or looking to refine your skills, this book covers all the core topics, including asynchronous programming, file handling, HTTP servers, and much more.</p>
<p>As a special contribution, <a target="_blank" href="https://github.com/Eomm/"><strong>Manuel Spigolon</strong></a> had the pleasure of writing a chapter dedicated to the latest <strong>Fastify v5</strong>. Fastify is one of the fastest Node.js web frameworks, and in this chapter, you'll learn how to use it to build robust APIs effortlessly.</p>
<p>If you're eager to dive into Node.js or explore Fastify v5, you can <strong>preorder the book today</strong> at <a target="_blank" href="https://packt.link/0mNGV">https://packt.link/0mNGV</a>.</p>
<p>Don’t miss it!</p>
]]></content:encoded></item><item><title><![CDATA[How to implement video streaming with Fastify]]></title><description><![CDATA[In April 2021, I tweeted about a quick experiment where I streamed a large GoPro video using Fastify.
That initial experiment sparked some interest, and just a few months ago, a user reached out with an intriguing question: how could they modify the ...]]></description><link>https://backend.cafe/how-to-implement-video-streaming-with-fastify</link><guid isPermaLink="true">https://backend.cafe/how-to-implement-video-streaming-with-fastify</guid><category><![CDATA[fastify]]></category><category><![CDATA[video streaming]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sat, 31 Aug 2024 05:00:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/t40Riz-hCDw/upload/34d673aa80e99f6f870a09dd45fe9040.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In April 2021, <a target="_blank" href="https://x.com/ManuEomm/status/1385694984880406528">I tweeted</a> about a quick experiment where I streamed a large GoPro video using Fastify.</p>
<p>That initial experiment sparked some interest, and just a few months ago, a user reached out with an intriguing question: how could they modify the code to support stream segmentation? Specifically, they wanted users to be able to jump to different parts of the video, effectively allowing timeline scrubbing during playback. I considered this a fascinating challenge and decided to turn it into a blog post.</p>
<p>In this article, we'll explore how to implement video streaming with Fastify, focusing on handling range requests. This will enable smooth playback even with large video files, allowing users to seek through the video timeline effortlessly.</p>
<p>By the end of this post, you'll understand the key concepts behind video streaming in Node.js, how to use Fastify to serve video content efficiently, and how to handle range requests to enhance the user experience with precise control over video playback.</p>
<h2 id="heading-setting-up-the-project">Setting up the project</h2>
<p>Before we dive into the code, let's set up our project environment. We'll be using Node.js 20 and Fastify v4 for this tutorial. Additionally, you'll need a large video file to stream — I'll be using a GoPro video I filmed while riding a jet ski with my fiancée 😄</p>
<p>Here's how you can create and set up the project. I've also included some additional plugins we'll need to complete the exercise:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new directory for the project</span>
mkdir fastify-video-streaming
<span class="hljs-built_in">cd</span> fastify-video-streaming

<span class="hljs-comment"># Initialize a new Node.js project with ES6 module support</span>
npm init es6 --yes

<span class="hljs-comment"># Install Fastify and necessary plugins</span>
npm install fastify@4 fastify-range@1
</code></pre>
<p>With the project initialised and dependencies installed, we're ready to start building the video streaming server.</p>
<h2 id="heading-getting-ready-to-stream">Getting ready to stream</h2>
<p>With our project set up, it's time to start coding. We'll begin by initialising the Fastify application and setting up a basic route to serve an HTML page containing a video tag. This HTML page will be the frontend interface for our video streaming where users can play and seek through the video content.</p>
<p>First, create a new file named <code>app.js</code> in your project directory. Now, let's write the code to initialise the Fastify server and set up the root route to serve the HTML page:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> Fastify <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify'</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">'node:fs'</span>

<span class="hljs-comment">// Initialize the Fastify application with logging enabled</span>
<span class="hljs-keyword">const</span> app = Fastify({ <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span> })

<span class="hljs-comment">// Set up a route to serve the HTML page</span>
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// Serve the index.html file from the project directory</span>
  <span class="hljs-keyword">return</span> fs.createReadStream(<span class="hljs-string">'./index.html'</span>)
})

<span class="hljs-comment">// Start the server and listen on port 8080</span>
<span class="hljs-keyword">await</span> app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">8080</span> })
</code></pre>
<p>Next, create an <code>index.html</code> file in the same directory to define the structure of our frontend and add the following <strong>basic</strong> code:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Video Streaming with Fastify<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Fastify Video Streaming<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">video</span> <span class="hljs-attr">controls</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"600"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/video-streaming"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"video/mp4"</span>&gt;</span>
        Your browser does not support the video tag.
    <span class="hljs-tag">&lt;/<span class="hljs-name">video</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This simple HTML page includes a video element that will request the video stream from our server via the <code>/video-streaming</code> route, which we'll implement next.</p>
<p>At this point, your Fastify server is set up to serve an HTML page that will act as the frontend for our video streaming application.</p>
<p>By running the <code>node --watch app.js</code> command in your terminal, you can start the server and access the page at <a target="_blank" href="http://localhost:8080"><code>http://localhost:8080</code></a> in your browser. You should see a basic page with a video player ready to stream content.</p>
<h2 id="heading-streaming-video-content">Streaming video content</h2>
<p>With the basic setup in place, it's time to implement the core functionality of our streaming server: handling video content with support for range requests. This is what allows users to seek through the video timeline, providing a smooth and responsive playback experience.</p>
<h3 id="heading-understanding-range-requests">Understanding range requests</h3>
<p>The <code>Range</code> HTTP header is essential for streaming large files like videos. It allows the client (in this case, the browser) to request specific portions of a file rather than downloading the entire file in one go. This functionality is particularly useful for video streaming, as it lets users jump to different parts of the video without having to load the entire file.</p>
<p>The range request feature is defined by <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7233">RFC 7233</a>. When a browser sends a request for a video, it typically includes a <code>Range</code> header specifying the byte range it wants.</p>
<p>An example of a <code>Range</code> header might look like this:</p>
<pre><code class="lang-plaintext">Range: bytes=0-
</code></pre>
<p>The client is asking for the video file starting from byte 0 to the end. If the server supports range requests, it responds with a <code>206 Partial Content</code> status and includes the requested byte range in the <code>Content-Range</code> header. Here's an example of the response headers:</p>
<pre><code class="lang-plaintext">Content-Range: bytes 0-1000000/2257069623
</code></pre>
<p>This header indicates that the server is sending bytes 0 to 1 MB of a total of 2.257.069.623 (&gt;2GB!) bytes in the video file.</p>
<p>Now the browser knows how much of the video it has received, and it can request additional segments as needed without buffering the entire video. So, only when needed, the browser will request the next segment of the video such asking for bytes from 1MB:</p>
<pre><code class="lang-plaintext">Range: bytes=1000001-
</code></pre>
<p>Note that the <code>Range</code> header is typically used to request byte-ranges, but it can also be used to implement a pagination-like system where the range is a slice of a list of items — such as a range of users — because the specification allows custom units! This was an <a target="_blank" href="https://stackoverflow.com/q/21765555/3309466">interesting topic on StackOverflow</a>, and I am mentioning it because it can be an interesting exercise for the mind to compare and contrast different ideas between developers!</p>
<h3 id="heading-implementing-the-video-streaming-route">Implementing the <code>/video-streaming</code> route</h3>
<p>Let’s add the <code>/video-streaming</code> route to our Fastify application to handle these range requests.</p>
<p>First, we must use the <code>fastify-range</code> plugin to easily parse the range header and extract the requested byte range. This plugin will add a request decorator to help us handle the range requests efficiently.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> fastifyRange <span class="hljs-keyword">from</span> <span class="hljs-string">'fastify-range'</span>

<span class="hljs-comment">// ...</span>
app.register(fastifyRange)
<span class="hljs-comment">// ...</span>
</code></pre>
<p>Now we can focus on the <code>/video-streaming</code> route implementation. Note that the route name was defined in the <code>src</code> attribute of the <code>&lt;source&gt;</code> tag in the <code>index.html</code> file.</p>
<pre><code class="lang-js">app.get(<span class="hljs-string">'/video-streaming'</span>, <span class="hljs-keyword">async</span> (request, reply) =&gt; {
  <span class="hljs-keyword">const</span> videoPath = <span class="hljs-string">'/path/to/your/video.mp4'</span>
  <span class="hljs-keyword">const</span> videoSize = fs.statSync(videoPath).size

  <span class="hljs-comment">// Extract the range from the request headers</span>
  <span class="hljs-keyword">const</span> range = request.range(videoSize)
  request.log.info({ range })
  <span class="hljs-keyword">if</span> (!range) {
    <span class="hljs-comment">// If no valid range is found, throw a 416 error</span>
    <span class="hljs-comment">// as indicated by the RFC 7233</span>
    <span class="hljs-keyword">const</span> error = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Range Not Satisfiable'</span>)
    error.statusCode = <span class="hljs-number">416</span>
    <span class="hljs-keyword">throw</span> error
  }

  <span class="hljs-comment">// Handle only the first range requested</span>
  <span class="hljs-keyword">const</span> singleRange = range.ranges[<span class="hljs-number">0</span>]

  <span class="hljs-comment">// Define the size of the chunk to send</span>
  <span class="hljs-keyword">const</span> chunkSize = <span class="hljs-number">1</span> * <span class="hljs-number">1e6</span> <span class="hljs-comment">// 1MB</span>
  <span class="hljs-keyword">const</span> { start } = singleRange
  <span class="hljs-keyword">const</span> end = <span class="hljs-built_in">Math</span>.min(start + chunkSize, videoSize - <span class="hljs-number">1</span>)
  <span class="hljs-keyword">const</span> contentLength = end - start + <span class="hljs-number">1</span>

  <span class="hljs-comment">// Set the appropriate headers for range requests</span>
  reply.headers({
    <span class="hljs-string">'Accept-Ranges'</span>: <span class="hljs-string">'bytes'</span>,
    <span class="hljs-string">'Content-Range'</span>: <span class="hljs-string">`bytes <span class="hljs-subst">${start}</span>-<span class="hljs-subst">${end}</span>/<span class="hljs-subst">${videoSize}</span>`</span>,
    <span class="hljs-string">'Content-Length'</span>: contentLength
  })

  <span class="hljs-comment">// Send a 206 Partial Content status code</span>
  reply.code(<span class="hljs-number">206</span>)
  reply.type(<span class="hljs-string">'video/mp4'</span>)

  <span class="hljs-comment">// Stream the requested chunk of the video file</span>
  <span class="hljs-keyword">return</span> fs.createReadStream(videoPath, { start, end })
})
</code></pre>
<p>The code above does the following:</p>
<ol>
<li><p><strong>Extracting the range:</strong> When the browser requests the video, it sends a <code>Range</code> header indicating the portion of the file it wants to download. We extract this range using the <code>request.range(videoSize)</code> method, which is provided by the <code>fastify-range</code> plugin. Note that we need to know the total size of the video file to validate the range because the browser might request a range that exceeds the file size.</p>
</li>
<li><p><strong>Validating the range:</strong> If no valid range is provided or the range is unsatisfiable, the server responds with a <code>416 Range Not Satisfiable</code> error. This indicates that the server cannot fulfil the request as specified.</p>
</li>
<li><p><strong>Chunk size and byte ranges:</strong> We calculate the size of the chunk to send based on the requested range. In this case, we’ve set the chunk size to 1MB and it is up to the server implementation to choose the appropriate size. You may implement a more complex logic to determine the chunk size based on the client's bandwidth or device. The <code>start</code> and <code>end</code> variables define the exact byte range that will be streamed back to the client.</p>
</li>
<li><p><strong>Setting headers:</strong> The server responds with a <code>206 Partial Content</code> status and includes headers like <code>Content-Range</code> and <code>Content-Length</code> to inform the client of the specific byte range being sent.</p>
</li>
<li><p><strong>Streaming the video:</strong> Finally, the server streams the requested portion of the video file using <code>fs.createReadStream(videoPath, { start, end })</code>. This allows the video player in the browser to play the video while additional portions are requested as needed.</p>
</li>
</ol>
<p>With this implementation, our Fastify server is now capable of streaming video content efficiently, handling range requests to provide a seamless playback experience for users.</p>
<p>The HTML <code>&lt;video&gt;</code> tag in our <code>index.html</code> file automatically handles range requests when a user interacts with the video's timeline controls. For instance, if a user clicks ahead to a different part of the video, the browser sends a new request with an updated <code>Range</code> header, prompting the server to deliver the corresponding video segment.</p>
<p>This mechanism ensures smooth playback and efficient use of bandwidth, as only the necessary parts of the video are loaded and played.</p>
<p>By implementing this route, we’ve enabled our server to stream large video files efficiently, providing users with the ability to navigate through the video timeline seamlessly.</p>
<p><img src="https://github.com/Eomm/blog-posts/blob/master/bonus/video-streaming/fastify-video-streaming.gif?raw=true" alt="Video example" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we explored how to build a video streaming server using Fastify and Node.js. We implemented a Fastify application capable of serving large video files with support for range requests. This approach allows users to interact with the video timeline, seamlessly streaming only the necessary parts of the video. The source code for this project is available on <a target="_blank" href="https://github.com/Eomm/blog-posts/tree/HEAD/bonus/video-streaming/">GitHub</a>.</p>
<p>I want to point out that this implementation is a basic example to get you started with video streaming in Fastify. In a real-world scenario, we should consider additional features like caching, security, and performance optimisations to ensure a robust and reliable video streaming service, but now you have the tools to implement video streaming in your own projects, allowing users to enjoy a smooth and interactive video experience.</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Resume data replication in Postgres and Node.js]]></title><description><![CDATA[This article is a continuation of Real-time data Replication in Postgres and Node.js. Before reading this article, I recommend you read the previous one because it provides essential context to the points I cover.
In our previous article, we discusse...]]></description><link>https://backend.cafe/resume-data-replication-in-postgres-and-nodejs</link><guid isPermaLink="true">https://backend.cafe/resume-data-replication-in-postgres-and-nodejs</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[replicaset]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Mon, 22 Jul 2024 05:00:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3yd8oXGoLqM/upload/df57c0d93041db90c77c19393cfd2d6d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article is a continuation of <a target="_blank" href="https://backend.cafe/real-time-data-replication-in-postgres-and-nodejs">Real-time data Replication in Postgres and Node.js</a>. Before reading this article, I recommend you read the previous one because it provides essential context to the points I cover.</p>
<p>In our previous article, we discussed how to replicate data from a Postgres database to a Node.js application in real-time using logical replication. However, if the Node.js application crashes or stops for some reason, the replication will cease, and we risk losing the data that our system produces in the meantime via another microservice or application.</p>
<p>In this article, I discuss how to resume replication from the last point where the Node.js application stopped by using a persistent replication slot in the Postgres database. This ensures that our application doesn't lose events produced by other microservices or applications during downtime.</p>
<h2 id="heading-creating-a-replication-slot">Creating a replication slot</h2>
<p>To resume replication, we need to create a replication slot in the Postgres database. A replication slot is a logical entity that keeps track of changes happening in the database and sends them to the subscriber. The <code>postgres</code> package we used in the previous article automatically created a replication slot for us, but it was not persistent, it was a <a target="_blank" href="https://www.postgresql.org/docs/16/view-pg-replication-slots.html"><code>TEMPORARY</code></a> replication slot that was removed when the subscriber disconnected.</p>
<p>Since we want to resume replication from the last point where the Node.js application was stopped, we need to create a persistent replication slot. Let's create one in a new <code>setup-replication.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>
<span class="hljs-keyword">const</span> { Client } = pg

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({ <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">'foopsw'</span> })
<span class="hljs-keyword">await</span> client.connect()

<span class="hljs-keyword">await</span> createReplicationSlotIfNotExists(<span class="hljs-string">'foo_slot'</span>)

<span class="hljs-keyword">await</span> client.end()

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createReplicationSlotIfNotExists</span> (<span class="hljs-params">slotName</span>) </span>{
  <span class="hljs-keyword">const</span> slots = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'SELECT * FROM pg_replication_slots WHERE slot_name = $1'</span>, [slotName])

  <span class="hljs-keyword">if</span> (!slots.rows.length) {
    <span class="hljs-keyword">const</span> newSlot = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">"SELECT * FROM pg_create_logical_replication_slot($1, 'pgoutput')"</span>, [slotName])
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Created replication slot'</span>, newSlot.rows[<span class="hljs-number">0</span>])
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Slot already exists'</span>, slots.rows[<span class="hljs-number">0</span>])
  }
}
</code></pre>
<p>We are using the <a target="_blank" href="https://www.postgresql.org/docs/16/view-pg-replication-slots.html"><code>pg_replication_slots</code></a> view to check whether a replication slot with the given name already exists. If it doesn't exist then we create a new replication slot using the <a target="_blank" href="https://www.postgresql.org/docs/16/functions-admin.html#FUNCTIONS-REPLICATION"><code>pg_create_logical_replication_slot</code></a> function.</p>
<p>Note that we specified the <a target="_blank" href="https://www.postgresql.org/docs/16/protocol-logical-replication.html"><code>pgoutput</code> plugin</a> in the function to decode the changes in the replication slot. This is the default plugin for logical replication, and it ships with Postgres. Be aware that there are other plugins, such as:</p>
<ul>
<li><p><a target="_blank" href="https://www.postgresql.org/docs/16/test-decoding.html"><code>test_decoding</code> plugin</a> is the simplest plugin that ships with Postgres to start building your own custom plugin.</p>
</li>
<li><p><a target="_blank" href="https://packages.ubuntu.com/noble/postgresql-16-wal2json"><code>wal2json</code></a>, which must be installed separately in the Postgres database, allowing you to use them in the <code>pg_create_logical_replication_slot</code> function.</p>
</li>
</ul>
<p>Note that each plugin has its own advantages and disadvantages, so choose the one that best fits your use case. The biggest difference if you try to use <code>test_decoding</code> versus <code>pgoutput</code> is that the former does not accept a publication name as a parameter while the <a target="_blank" href="https://github.com/postgres/postgres/blob/3c469a939cf1cc95b136653e7c6e27e472dc0472/src/backend/replication/pgoutput/pgoutput.c#L449-L452">latter does</a>. This means that you can use <code>pgoutput</code> to filter the changes you want to replicate, while <code>test_decoding</code> will replicate all changes in the database without filtering them!</p>
<p>Now, run the <code>setup-replication.js</code> file to create a replication slot!</p>
<h2 id="heading-configuring-the-consumer">Configuring the consumer</h2>
<p>In the previous article, we already created the <code>setup-consumer.js</code> that creates the publications that our application is interested in. So, we can reuse the same file and just run it — if we haven't already.</p>
<p>As a reminder: you will first need to start the Postgres server and create the <code>foo</code> database.</p>
<h2 id="heading-resuming-the-replication">Resuming the replication</h2>
<p>We are ready to create a new <code>consumer-resume.js</code> file that will resume replication from the last point where the Node.js application was stopped, so let's jump into it:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { LogicalReplicationService, PgoutputPlugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'pg-logical-replication'</span>

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> LogicalReplicationService({
  <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">'foopsw'</span>
}, { <span class="hljs-attr">acknowledge</span>: { <span class="hljs-attr">auto</span>: <span class="hljs-literal">false</span> } })

client.on(<span class="hljs-string">'data'</span>, <span class="hljs-keyword">async</span> (lsn, log) =&gt; {
  <span class="hljs-keyword">if</span> (log.tag === <span class="hljs-string">'insert'</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${lsn}</span>) Received insert: <span class="hljs-subst">${log.relation.schema}</span>.<span class="hljs-subst">${log.relation.name}</span> <span class="hljs-subst">${log.<span class="hljs-keyword">new</span>.id}</span>`</span>)
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (log.relation) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${lsn}</span>) Received log: <span class="hljs-subst">${log.relation.schema}</span>.<span class="hljs-subst">${log.relation.name}</span> <span class="hljs-subst">${log.tag}</span>`</span>)
  }

  <span class="hljs-keyword">await</span> client.acknowledge(lsn)
})

<span class="hljs-keyword">const</span> eventDecoder = <span class="hljs-keyword">new</span> PgoutputPlugin({
  <span class="hljs-comment">// Get a complete list of available options at:</span>
  <span class="hljs-comment">// https://www.postgresql.org/docs/16/protocol-logical-replication.html</span>
  <span class="hljs-attr">protoVersion</span>: <span class="hljs-number">4</span>,
  <span class="hljs-attr">binary</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">publicationNames</span>: [
    <span class="hljs-string">'foo_odd'</span>,
    <span class="hljs-string">'foo_update_only'</span>
  ]
})

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Listening for changes...'</span>)
process.on(<span class="hljs-string">'SIGINT'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Stopping client...'</span>)
  <span class="hljs-keyword">await</span> client.stop()
})

<span class="hljs-keyword">await</span> client.subscribe(eventDecoder, <span class="hljs-string">'foo_slot'</span>)
</code></pre>
<p>This time, we are using the <a target="_blank" href="https://www.npmjs.com/package/pg-logical-replication"><code>pg-logical-replication</code></a> package to demonstrate the resuming of replication. Its low-level API provides us more control over the replication process, otherwise we would not be able to configure the Plugin to receive only the changes we are interested in.</p>
<p>The code can be explained as follows:</p>
<ol>
<li><p>We are creating a new <code>LogicalReplicationService</code> instance and passing the connection options to it. Note that we are setting the <a target="_blank" href="http://acknowledge.auto"><code>acknowledge.auto</code></a> option to <code>false</code> to manually acknowledge the changes; otherwise, they would be automatically acknowledged. By setting this option to <code>false</code>, we gain even more control over the process.</p>
</li>
<li><p>We are listening to the <code>data</code> event to receive changes from the replication slot.</p>
<ul>
<li><p>At this point, you should process the <code>log</code> and apply your business logic. In this case, we're just logging the changes to the console.</p>
</li>
<li><p>After processing the changes, you must acknowledge them using the <code>acknowledge</code> method; otherwise, the slot will not advance. The <code>lsn</code> (<strong>Log Sequence Number</strong>) is the unique identifier for each change in the database and is used to track changes in the replication slot.</p>
</li>
</ul>
</li>
<li><p>We are creating a new <code>PgoutputPlugin</code> instance and passing it to the <code>subscribe</code> method to establish a connection with the replication slot.</p>
</li>
</ol>
<p>To start the application, run the <code>node consumer-resume.js</code> file, and it will begin receiving changes from the replication slot. If we did all the steps correctly, you can start the <code>node producer.js</code> file that we wrote in the previous article to produce changes in the database and see the changes in the consumer application.</p>
<p>If you stop the consumer application by pressing <code>Ctrl+C</code>, the replication will stop, and the slot will not move forward. However, if you start the <code>consumer-resume.js</code> application again, it will resume replication from the last point where it was stopped! 🎉</p>
<p>Moreover, we can see that the output shows only the changes from the <code>foo_odd</code> and <code>foo_update_only</code> publications, which we configured in the <code>PgoutputPlugin</code> instance so we will see updates and inserts with odd <code>id</code> numbers only:</p>
<pre><code class="lang-sh">0/15648E0) Received insert: public.foo 18
0/15649A0) Received <span class="hljs-built_in">log</span>: public.foo update
0/1564AF0) Received <span class="hljs-built_in">log</span>: public.foo update
0/1564B80) Received insert: public.foo 20
0/1564C40) Received <span class="hljs-built_in">log</span>: public.foo update
0/1564D90) Received <span class="hljs-built_in">log</span>: public.foo update
0/1564E20) Received insert: public.foo 22
0/1564EE0) Received <span class="hljs-built_in">log</span>: public.foo update
0/1565030) Received <span class="hljs-built_in">log</span>: public.foo update
0/15650C0) Received insert: public.foo 24
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we discussed how to resume replication from the last point where the Node.js application was stopped and all the code is save in <a target="_blank" href="https://github.com/Eomm/blog-posts">this repository</a>.</p>
<p>We created a persistent replication slot in the Postgres database and used the <code>pg-logical-replication</code> package to demonstrate resuming replication. This ensures that our application doesn't lose data produced by other microservices or applications during downtime.</p>
<p>In doing so, we did not change the <code>producer.js</code> file, which means that the producer can continue to produce changes in the database without any issues and the previous Publications setup is still valid: we just configured manually the replication slot and the new consumer.</p>
<p>Remember, the replication slot retains changes in the database until the slot is dropped or the changes are acknowledged by the subscriber. If not managed properly, this can lead to high disk usage because Postgres will keep the changes in the WAL logs indefinitely instead of removing them.</p>
<p>I hope you enjoyed this article and learned something new! Does it deserve a comment and a share? 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Real-time data replication in Postgres and Node.js]]></title><description><![CDATA[In today's fast-paced digital landscape, businesses rely heavily on data to drive decision-making processes. Real-time data replication has emerged as a critical capability for organisations seeking to stay ahead of the curve, enabling them to synchr...]]></description><link>https://backend.cafe/real-time-data-replication-in-postgres-and-nodejs</link><guid isPermaLink="true">https://backend.cafe/real-time-data-replication-in-postgres-and-nodejs</guid><category><![CDATA[Node.js]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[realtime]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Fri, 24 May 2024 06:50:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xYO4F6HoxOQ/upload/9852c4651b5825b92e83124c93670379.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's fast-paced digital landscape, businesses rely heavily on data to drive decision-making processes. Real-time data replication has emerged as a critical capability for organisations seeking to stay ahead of the curve, enabling them to synchronise data across multiple databases seamlessly. To make a few practical examples, real-time data replication is essential for scenarios such as:</p>
<ul>
<li><p>Keeping different databases in sync, which is a typical scenario when a company acquires another company and needs to merge the data.</p>
</li>
<li><p>Relying on a database to trigger events in other systems; for example, when a new user is created in the database, a welcome email is sent without modifying existing code or introducing new architecture components like message queues.</p>
</li>
<li><p>Keeping an application's in-memory cache in sync with the database to improve the performance of read queries.</p>
</li>
<li><p>For high availability and disaster recovery scenarios.</p>
</li>
</ul>
<p>As you can see, real-time data replication is a powerful tool that can be used in various scenarios to improve the performance, reliability and scalability of your applications.</p>
<p>In this blog post, we'll explore the power of Postgres replication management, to understand how to do a real-time data replication in Postgres with Node.js.<br /><em>You will need to run some Docker commands to set up a Postgres instance with logical replication enabled, but it is a copy-paste operation.</em></p>
<h2 id="heading-introduction-to-postgres-replication-management">Introduction to Postgres replication management</h2>
<p>PostgreSQL is a powerful open-source relational database system known for its robust features and extensibility. One of its standout capabilities is <strong>replication management</strong>, allowing you to replicate data from one database to another in real-time. This feature is invaluable to organisations that require characteristics such as:</p>
<ul>
<li><p>High availability: the primary database fails, and a standby server takes over seamlessly.</p>
</li>
<li><p>Load balancing: distributing read queries across multiple replicas to improve performance.</p>
</li>
<li><p>Data distribution: synchronising data across geographically distributed systems.</p>
</li>
</ul>
<p>Postgres offers various mechanisms to support the listed scenarios where each solution has its strengths and use cases. To list a few:</p>
<ul>
<li><p>Synchronous multimaster replication: a query must be committed on all nodes before returning to the client. Very high availability and data consistency but with a performance cost.</p>
</li>
<li><p>Write-ahead Log Shipping (WAL): the primary server sends WAL segments to standby servers, which replay them to stay in sync. It's a simple and reliable solution but with a potential for data loss.</p>
</li>
<li><p>Logical Replication: replicates individual changes (inserts, updates, deletes) rather than entire WAL segments. It's suitable for scenarios such as data warehousing, data distribution and selective replication. Moreover, it allows for more granular control over which tables and changes are replicated and it is possible to replicate data between different versions of Postgres or multiple sources!</p>
</li>
</ul>
<p>The complete list of replication mechanisms can be found in the <a target="_blank" href="https://www.postgresql.org/docs/16/different-replication-solutions.html">Postgres documentation</a>.</p>
<p>In this blog post, we'll focus on <a target="_blank" href="https://www.postgresql.org/docs/16/logical-replication.html"><strong>Logical Replication</strong></a> mechanism.</p>
<h3 id="heading-the-wal-file">The WAL file</h3>
<p>The Write-ahead Log (WAL) is a fundamental concept in Postgres replication and we mentioned it in the previous section. The WAL is a file that stores all changes made to the database, such as inserts, updates and deletes, in a sequential manner. We could say that the WAL is our source of truth, and this file is handled by Postgres to guarantee data consistency and <a target="_blank" href="https://www.postgresql.org/docs/16/wal-reliability.html">durability</a> in case of a crash or disk failure.</p>
<p>We must discuss the WAL because it is the foundation of Postgres replication. We can configure Postgres to handle the WAL file, and we can use it to replicate data across multiple databases. So we must understand the <a target="_blank" href="https://www.postgresql.org/docs/16/runtime-config-wal.html#GUC-WAL-LEVEL"><code>wal_level</code></a> configuration parameter. This parameter defines how much information is written to the WAL file. The possible values are:</p>
<ul>
<li><p><code>minimal</code>: only the information needed to recover from a crash is written to the WAL file.</p>
</li>
<li><p><code>replica</code>: the default value, it writes enough information to support WAL archiving and replication.</p>
</li>
<li><p><code>logical</code>: it stores the same information as <code>replica</code> and additional information needed for logical replication, so this configuration requires more disk space.</p>
</li>
</ul>
<h2 id="heading-developing-a-real-time-data-replication-system-with-nodejs">Developing a real-time data replication system with Node.js</h2>
<p>To develop a real-time data replication system with Node.js, we must follow these steps:</p>
<ol>
<li><p>Initialise the Postgres database.</p>
</li>
<li><p>Create a data producer we want to replicate.</p>
</li>
<li><p>Create a data consumer that replicates the data through the Postgres replication mechanism.</p>
</li>
</ol>
<p>So let's start!</p>
<h3 id="heading-setting-up-postgres">Setting up Postgres</h3>
<p>We can start our journey into Postgres replication management by starting a Postgres instance and configuring it to support logical replication. Let's use Docker for this task:</p>
<pre><code class="lang-bash">docker run \
  --name demo-postgres \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=foopsw \
  postgres:16 \
  postgres -c <span class="hljs-string">"wal_level=logical"</span>
</code></pre>
<p>This command will start a Postgres instance with logical replication enabled, and you can connect to it using the following command:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it demo-postgres psql -U postgres
</code></pre>
<p>Now, from the Postgres shell, we can create a database and a table to work with:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> foo (
  <span class="hljs-keyword">id</span> <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span>,
  col1 <span class="hljs-built_in">VARCHAR</span>,
  col2 <span class="hljs-built_in">INT</span>
);
</code></pre>
<p>To check if the table was created successfully, the following command lists the user's tables in the database:</p>
<pre><code class="lang-sql">\dt
</code></pre>
<h3 id="heading-creating-a-data-producer">Creating a data producer</h3>
<p>Now that we have our Postgres instance set up, let's create a Node.js application that acts as a data producer. This application will insert data into the <code>foo</code> table we created earlier and represent the source of data replication. It could be a web application, an IoT device, or any other data source you may have.</p>
<p>Let's create a <code>producer.js</code> file and implement the following logic:</p>
<ul>
<li><p>It connects to the Postgres database.</p>
</li>
<li><p>Every second, it inserts a new row into the <code>foo</code> table or updates the last inserted row alternatively.</p>
</li>
<li><p>It listens for a <code>SIGINT</code> signal to gracefully close the connection.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span> <span class="hljs-comment">// ℹ️ npm install pg</span>
<span class="hljs-keyword">const</span> { Client } = pg

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
  <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">'foopsw'</span>
})

<span class="hljs-keyword">await</span> client.connect()

<span class="hljs-keyword">let</span> counter = <span class="hljs-number">0</span>
<span class="hljs-keyword">let</span> lastId = <span class="hljs-number">0</span>
<span class="hljs-keyword">const</span> fakeUserInteraction = <span class="hljs-built_in">setInterval</span>(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">if</span> (counter % <span class="hljs-number">2</span> === <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'INSERT INTO foo (col1, col2) VALUES ($1, $2) RETURNING id'</span>, [<span class="hljs-string">'Hello world!'</span>, counter])
    lastId = res.rows[<span class="hljs-number">0</span>].id
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Inserted new row: <span class="hljs-subst">${lastId}</span>`</span>)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'UPDATE foo SET col1 = $1, col2 = $2 WHERE id = $3'</span>, [<span class="hljs-string">'Hello world!!!'</span>, <span class="hljs-number">42</span>, lastId])
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Updated row <span class="hljs-subst">${lastId}</span>`</span>)
  }

  counter++
}, <span class="hljs-number">1000</span>)

process.on(<span class="hljs-string">'SIGINT'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-built_in">clearInterval</span>(fakeUserInteraction)
  <span class="hljs-keyword">await</span> client.end()
})
</code></pre>
<p>To run this code the <code>node producer.js</code> command is enough.</p>
<h3 id="heading-creating-a-data-consumer">Creating a data consumer</h3>
<p>The data consumer is the other side of the replication process. It can be another Postgres instance, or even a different a different database engine. Postgres allows us to replicate the data seamlessly across different versions of the Postgres database just by configuring them accordingly (not the topic of this blog post, but there are good <a target="_blank" href="https://www.youtube.com/watch?v=3Z4Hhh5EnLA">video tutorials</a> for this use case).</p>
<p>Our use case is to replicate the data to a Node.js application! This application will connect to the Postgres database and listen for changes in the <code>foo</code> table using the logical replication mechanism.</p>
<h4 id="heading-setting-up-the-data-consumer">Setting up the data consumer</h4>
<p>To set up the data consumer, we must introduce some new Postgres concepts:</p>
<ul>
<li><p><a target="_blank" href="https://www.postgresql.org/docs/16/logical-replication-publication.html"><strong>Publication</strong></a>: A publication defines a set of tables and changes to be replicated. We must create a publication to specify which tables and changes are replicated.</p>
</li>
<li><p><a target="_blank" href="https://www.postgresql.org/docs/16/logical-replication-subscription.html"><strong>Subscription</strong></a>: A subscription defines the source of replication (e.g., primary server) and the target replica(s). We must create a subscription to subscribe to changes happening in the <code>foo</code> table.</p>
</li>
<li><p><a target="_blank" href="https://www.postgresql.org/docs/16/warm-standby.html#STREAMING-REPLICATION-SLOTS"><strong>Replication slot</strong></a>: A replication slot is like a bookmark in the WAL file that keeps track of the last WAL segment read by a client. It forces the primary server to retain the WAL segments required by any client that is using the replication slot.</p>
</li>
</ul>
<p>We need to create all these objects in the Postgres database before we can start listening for changes. Let's do it programmatically with Node.js by creating a <code>setup-consumer.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>
<span class="hljs-keyword">const</span> { Client } = pg

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({ <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">'foopsw'</span> })
<span class="hljs-keyword">await</span> client.connect()

<span class="hljs-keyword">await</span> createPublicationIfNotExists(<span class="hljs-string">'foo_even'</span>, <span class="hljs-string">'TABLE foo WHERE (id % 2 = 0);'</span>)
<span class="hljs-keyword">await</span> createPublicationIfNotExists(<span class="hljs-string">'foo_update_only'</span>, <span class="hljs-string">"TABLE foo WITH (publish = 'update');"</span>)

<span class="hljs-keyword">await</span> client.end()

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createPublicationIfNotExists</span> (<span class="hljs-params">pubName, condition</span>) </span>{
  <span class="hljs-keyword">const</span> pub = <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">'SELECT * FROM pg_publication WHERE pubname = $1'</span>, [pubName])

  <span class="hljs-keyword">if</span> (!pub.rows.length) {
    <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`CREATE PUBLICATION <span class="hljs-subst">${pubName}</span> FOR <span class="hljs-subst">${condition}</span>`</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Created publication <span class="hljs-subst">${pubName}</span>`</span>)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">await</span> client.query(<span class="hljs-string">`ALTER PUBLICATION <span class="hljs-subst">${pubName}</span> SET <span class="hljs-subst">${condition}</span>`</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Updated publication <span class="hljs-subst">${pubName}</span>`</span>)
  }
}
</code></pre>
<p>The code shows how to create and update a publication. When a publication is created, it specifies:</p>
<ol>
<li>Which tables and columns are included in the replication process?</li>
</ol>
<ul>
<li><p>If no columns are specified, all columns are included.</p>
</li>
<li><p>It is possible to list multiple tables or set <code>FOR ALL TABLES;</code> to include all tables in the replication process.</p>
</li>
</ul>
<ol start="2">
<li>Which operations are included in the replication process?</li>
</ol>
<ul>
<li><p>The default is to include all operations (inserts, updates, deletes).</p>
</li>
<li><p>It is possible to specify only inserts, only updates, or only deletes.</p>
</li>
</ul>
<p>We have created two publications in the code snippet above:</p>
<ul>
<li><p><code>foo_even</code>: it includes only rows where the <code>id</code> is an even number.</p>
</li>
<li><p><code>foo_update_only</code>: it includes only updates.</p>
</li>
</ul>
<p>As shown, the publications are very flexible and can be tailored to specific use cases.</p>
<h4 id="heading-subscribing-to-changes">Subscribing to changes</h4>
<p>Finally, we are ready to create the <code>consumer.js</code> file to listen for changes in the <code>foo</code> table. To do so, we will use the <a target="_blank" href="https://www.npmjs.com/package/postgres"><code>postgres</code></a> module because it provides a simple way to consume the logical replication data changes or we should implement the <a target="_blank" href="https://www.postgresql.org/docs/16/protocol-logical-replication.html">logical replication protocol</a> ourselves. Alternatively, you can use the <a target="_blank" href="https://www.npmjs.com/package/pg-logical-replication"><code>pg-logical-replication</code></a> library — it depends on your preference.</p>
<p>To create the <code>consumer.js</code> file, we can use the following code snippet. So first we must connect to the Postgres database and list all the publications we want to subscribe to:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> postgres <span class="hljs-keyword">from</span> <span class="hljs-string">'postgres'</span>

<span class="hljs-keyword">const</span> sql = postgres({
  <span class="hljs-attr">debug</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">'foopsw'</span>,
  <span class="hljs-attr">publications</span>: [
    <span class="hljs-string">'foo_even'</span>,
    <span class="hljs-string">'foo_update_only'</span>
  ]
})
</code></pre>
<p>Then we can subscribe to the changes happening in the <code>foo</code> table:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> eventPattern = <span class="hljs-string">'*:foo'</span>
<span class="hljs-keyword">const</span> { unsubscribe } = <span class="hljs-keyword">await</span> sql.subscribe(
  eventPattern,
  <span class="hljs-function">(<span class="hljs-params">row, { command, relation, key, old }</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log({
      command,
      row
    })
  },

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onConnect</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Callback on initial connect and potential reconnects</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Connected to the publication'</span>)
  }
)

process.on(<span class="hljs-string">'SIGINT'</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> unsubscribe()
  process.exit()
})
</code></pre>
<p>If we run the <code>node consumer.js</code> command, we will see the <code>console.log</code> output every time the <code>producer.js</code>:</p>
<ul>
<li><p>Insert a row with an even ID in the <code>foo</code> table.</p>
</li>
<li><p>Update a row in the <code>foo</code> table.</p>
</li>
</ul>
<p>Note that the <a target="_blank" href="https://github.com/porsager/postgres?tab=readme-ov-file#subscribe-pattern"><code>eventPattern</code></a> is just a feature of the <code>postgres</code> module that allows us to filter the changes we are getting from the database. The main driver is and will always be the Postgres publication we created earlier with the <code>setup-consumer.js</code> script.</p>
<p>So far we did not create the Replication Slot, but the <code>postgres</code> module will do it for us by calling the <a target="_blank" href="https://www.postgresql.org/docs/16/protocol-replication.html#PROTOCOL-REPLICATION-CREATE-REPLICATION-SLOT"><code>CREATE_REPLICATION_SLOT</code></a> command creating a <a target="_blank" href="https://github.com/porsager/postgres/blob/cc688c642fc98c4338523d3e281e03bf0c3417b8/src/subscribe.js#L85"><code>TEMPORARY</code></a> slot. Every subscription needs a Replication Slot to track the changes that they are consuming.</p>
<p>Note that the Replication Slot MUST be deleted or consumed by the client, otherwise — if it isn't then the WAL file in the primary server will not be truncated and the disk space will grow indefinitely! Luckily, the <code>postgres</code> module creates a temporary replication slot that is automatically deleted when the connection is closed.</p>
<p>Now our Node.js application is ready to listen for changes in the <code>foo</code> table and replicate them in real-time!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>PostgreSQL's replication capabilities are a cornerstone of its reliability and scalability. We just scratched the surface of what is possible with Postgres replication management, but we have seen how to set up a real-time data replication system using Node.js and Postgres logical replication.</p>
<p>Another interesting use case is to use the logical replication mechanism to read all the changes happening in the database while the Node.js application was offline. This is possible by handling the Replication Slot manually and it can be done with the <a target="_blank" href="https://www.npmjs.com/package/pg-logical-replication#2-usage"><code>pg-logical-replication</code></a> module's API, but this will be the topic of another blog post!</p>
<p>We hope this blog post has given you a good starting point to explore Postgres replication management further. Here are some resources to deepen your knowledge:</p>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=WXKxwl9k5Q8">https://www.youtube.com/watch?v=WXKxwl9k5Q8</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=3Z4Hhh5EnLA">https://www.youtube.com/watch?v=3Z4Hhh5EnLA</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=E6W-ZWVRAHU">https://www.youtube.com/watch?v=E6W-ZWVRAHU</a></p>
</li>
</ul>
<p>Comment and share if you enjoyed this article!</p>
]]></content:encoded></item><item><title><![CDATA[Create a Telegram Bot with Fastify from Scratch]]></title><description><![CDATA[In today's digital landscape, chatbots have become a ubiquitous part of our online interactions.
These versatile and intelligent programs help automate tasks, answer queries, and provide a seamless conversational experience.
One popular platform for ...]]></description><link>https://backend.cafe/create-a-telegram-bot-with-fastify-from-scratch</link><guid isPermaLink="true">https://backend.cafe/create-a-telegram-bot-with-fastify-from-scratch</guid><category><![CDATA[telegram]]></category><category><![CDATA[fastify]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[bot]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Tue, 31 Oct 2023 07:03:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0E_vhMVqL9g/upload/dd020cd8be4127e13cb78343abc2a693.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's digital landscape, chatbots have become a ubiquitous part of our online interactions.
These versatile and intelligent programs help automate tasks, answer queries, and provide a seamless conversational experience.</p>
<p>One popular platform for developing chatbots is Telegram, known for its user-friendly interface and robust features. If you've ever wondered how to create your very own Telegram bot, you're in the right place!</p>
<p>Today, we will build a Telegram bot from the ground up using the Fastify framework. Fastify is a high-performance web framework for Node.js, making it an excellent choice for developing web applications and APIs. By the end of this blog post, you'll have a fully functional Telegram bot!</p>
<p>So, let's dive into the process of creating your own Telegram bot with Fastify!</p>
<h2 id="heading-setting-up-the-project">Setting up the Project</h2>
<p>Our journey begins by setting up the foundation for our Telegram bot project. We've chosen to leverage <a target="_blank" href="https://platformatic.dev/">Platformatic</a>, a powerful tool that accelerates project setup, allowing you to hit the ground running. With just a few simple commands, Platformatic creates a complete Fastify application, preconfigured and ready to use, seamlessly connected to the most famous RDBMS Databases (we will use SQLite). This not only saves you time but also provides a solid base for your Telegram bot project.</p>
<p>To get started, run the following command:</p>
<pre><code class="lang-bash">npm create -y platformatic@1
</code></pre>
<p>You will be prompted with a few questions about your project. Here are the answers we recommend:</p>
<pre><code class="lang-bash"> Hello Eomm, welcome to Platformatic 1.2.0!
 Let<span class="hljs-string">'s start by creating a new project.
? Which kind of project do you want to create? DB
? Where would you like to create your project? my-telegram-bot
? What database do you want to use? SQLite
? Do you want to use the connection string "sqlite://./db.sqlite"? Confirm
? What port do you want to use? 3042
? Do you want to run npm install? yes
? Do you want to create default migrations? yes
? Do you want to apply migrations? yes
? Do you want to create a plugin? yes
? Do you want to use TypeScript? no
? Do you want to create the github action to deploy this application to Platformatic Cloud? yes
? Do you want to enable PR Previews in your application? no</span>
</code></pre>
<p>Now, if we move to the <code>my-telegram-bot</code> folder, we can start the server with the following command:</p>
<pre><code class="lang-bash">npm start
</code></pre>
<p>You can access the application at <code>http://localhost:3042</code>, where you'll find the default Platformatic welcome page.</p>
<p>Exploring the project structure, we can see that Platformatic has created many files and folders for us.
Here is the most important ones:</p>
<ul>
<li><code>platformatic.db.json</code>: the configuration file of the project. It is the main file that Platformatic uses to understand how to build the project. You will recognize many Fastify's standard options here.</li>
<li><code>migrations/</code>: the folder that contains the database migrations. The cool thing is that based on the database schema, Platformatic will provide to the application an object to interact with the database!</li>
<li><code>plugins/</code>: the folder that contains the Fastify plugins. Platformatic will automatically register them for us!</li>
<li><code>routes/</code>: the folder that contains custom Fastify routes. Platformatic will automatically register them for us!</li>
<li><code>.env</code>: the file that contains the environment variables. Platformatic will automatically load them for us! <em>(if we name them with the `PLT</em>` prefix)_</li>
</ul>
<p>I like to say that if you know Fastify, you know Platformatic! Platformatic is <em>"just"</em> Fastify with batteries included!
Now that we have a solid foundation for our project, we can start building our Telegram bot!</p>
<h2 id="heading-implementing-the-telegram-bot">Implementing the Telegram bot</h2>
<p>Our first step on this Telegram bot creation journey involves setting up our bot on the Telegram platform.
To achieve this, we'll interact with <a target="_blank" href="https://t.me/BotFather">@BotFather</a>, a official Telegram bot that assists in creating and managing other bots.</p>
<ul>
<li>Start a chat with <a target="_blank" href="https://t.me/BotFather">@BotFather</a></li>
<li>Execute the <code>/newbot</code> command</li>
<li>Choose a name for your bot (e.g. <code>My Bot</code>)</li>
<li>Choose a username for your bot (e.g. <code>eommDemoBot</code>)</li>
</ul>
<p>Once you've completed these steps, you'll have obtained the token for your bot:</p>
<p><img src="https://github.com/Eomm/blog-posts/raw/6265cc133c4ebab7cadba08b11c33c3fc99ae815/posts/assets/bot-token-creation.png" alt="the botfather" /></p>
<p>Now, integrate this token into your project by adding the following line to the <code>.env</code> file:</p>
<pre><code class="lang-bash">PLT_TELEGRAM_BOT_TOKEN=PUT_HERE_YOUR_TOKEN
</code></pre>
<p>To simplify the integration, we'll use the <a target="_blank" href="https://npmjs.com/package/@eomm/fastify-telegram"><code>@eomm/fastify-telegram</code></a> package. You can install this package with the following command: <code>npm i @eomm/fastify-telegram</code>. </p>
<p>Next, rename the <code>plugins/example.js</code> file to <code>plugins/telegram.js</code> and add the provided JavaScript code. This code configures the <code>@eomm/fastify-telegram</code> package and sets up your bot to respond to messages:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fastifyTelegram = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@eomm/fastify-telegram'</span>)

<span class="hljs-comment">/** <span class="hljs-doctag">@param <span class="hljs-type">{import('fastify').FastifyInstance}</span> </span>app */</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">telegramBotPlugin</span> (<span class="hljs-params">app, opts</span>) </span>{
  <span class="hljs-keyword">await</span> app.register(fastifyTelegram, {
    <span class="hljs-attr">botToken</span>: app.platformatic.configManager.env.PLT_TELEGRAM_BOT_TOKEN,
    <span class="hljs-attr">decoratorBotName</span>: <span class="hljs-string">'bot'</span>,
    <span class="hljs-attr">waitForHealthPolling</span>: <span class="hljs-number">2</span>_000,
    <span class="hljs-attr">onUnhandledError</span>: <span class="hljs-keyword">async</span> (err, ctx) =&gt; {
      app.log.error(err, <span class="hljs-string">`Ooops, encountered an error for "<span class="hljs-subst">${ctx.updateType}</span>"`</span>)
      <span class="hljs-keyword">await</span> ctx.reply(<span class="hljs-string">'🙈 Ooops, something went wrong'</span>)
    }
  })

  app.bot.on(<span class="hljs-string">'text'</span>, <span class="hljs-keyword">async</span> (ctx) =&gt; {
    <span class="hljs-keyword">await</span> ctx.reply(<span class="hljs-string">'Hello World!'</span>)
  })
}
</code></pre>
<p>Let's analyze the code:</p>
<ol>
<li>We export a standard Fastify plugin. Platformatic will automatically register it for us! Note the JSDOC comment that loads the type definition of the Fastify instance and allows us to use the <code>app</code> object with autocompletion.</li>
<li>We register the <code>fastifyTelegram</code> plugin as a normal Fastify plugin.</li>
<li>We pass the <code>botToken</code> option to the <code>fastifyTelegram</code> plugin using the Platformatic's <code>configManager</code> utility. This option is required and is the token we got from the <code>BotFather</code>.</li>
<li>We pass the <code>decoratorBotName</code> option to customize the name of the decorator that will be injected into the Fastify application.</li>
<li>We pass the <code>onUnhandledError</code> option to the <code>fastifyTelegram</code> plugin. This option is optional and is a function that will be called when an unhandled error occurs on our Bot logic. The plugin will automatically catch the error and call this function to avoid a server crash.</li>
<li>Finally, we register a listener for the <code>text</code> event of the <code>app.bot</code>. This object is a <a target="_blank" href="https://telegrafjs.org/">Telegraf</a> bot instance that is automatically injected into the Fastify application by the <code>fastifyTelegram</code> plugin. You can customize the bot by adding listeners to the events you want to handle.</li>
</ol>
<p>Now we can start the server with <code>npm start</code> and if we open a new chat with our bot, we should see the <code>Hello World!</code> message!</p>
<p>Under the hood, the <code>@eomm/fastify-telegram</code> plugin initiates a <a target="_blank" href="https://github.com/pytopia/project-nashenas-telegram-bot/blob/main/Long%20Polling%20vs.%20Webhook.md">long polling</a> process against the Telegram API, waiting for new messages from the Telegram servers.</p>
<h2 id="heading-registering-users">Registering Users</h2>
<p>Now that we have a working Telegram bot, we can start implementing the business logic. Registering users is an essential part of this process. To achieve this, we will use Platformatic's utilities to interact with the database.</p>
<p>You'll need to rewrite the <code>migrations/001.do.sql</code> file with the provided SQL schema. This migration will create a <code>users</code> table, allowing you to store user information:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
  <span class="hljs-keyword">id</span> <span class="hljs-built_in">TEXT</span> PRIMARY <span class="hljs-keyword">KEY</span>,
  chat_id <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  full_name <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  username <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  lang <span class="hljs-built_in">CHAR</span>(<span class="hljs-number">2</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">'en'</span>,
  created_at <span class="hljs-built_in">TIMESTAMP</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>
);
</code></pre>
<p>This migration will create a <code>users</code> table automatically for us, just remember to delete the <code>db.sqlite</code> file before starting the server again.</p>
<p>Since the <code>routes/</code> folder contains the business logic of our application, we can create a new file called <code>routes/telegram-bot.js</code> and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-comment">/// &lt;reference path="../global.d.ts" /&gt;</span>
<span class="hljs-meta">'use strict'</span>

<span class="hljs-comment">/** <span class="hljs-doctag">@param <span class="hljs-type">{import('fastify').FastifyInstance}</span> </span>fastify */</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app, opts</span>) </span>{
  app.bot.use(<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">upsertUser</span> (<span class="hljs-params">ctx, next</span>) </span>{
    <span class="hljs-keyword">const</span> userId = ctx.update.message?.from.id || ctx.update.callback_query?.from.id
    <span class="hljs-keyword">const</span> chatId = ctx.update.message?.chat.id || ctx.update.callback_query?.message?.chat.id

    <span class="hljs-keyword">const</span> [user] = <span class="hljs-keyword">await</span> app.platformatic.entities.user.find({
      <span class="hljs-attr">where</span>: { <span class="hljs-attr">id</span>: { <span class="hljs-attr">eq</span>: userId } }
    })

    <span class="hljs-keyword">if</span> (user) {
      ctx.user = user
    } <span class="hljs-keyword">else</span> {
      app.log.info(<span class="hljs-string">'Creating new user'</span>, ctx.update.message.from)

      <span class="hljs-keyword">const</span> fullName = ctx.update.message.from.first_name + (ctx.update.message.from.last_name ? <span class="hljs-string">` <span class="hljs-subst">${ctx.update.message.<span class="hljs-keyword">from</span>.last_name}</span>`</span> : <span class="hljs-string">''</span>)
      <span class="hljs-keyword">const</span> [newUser] = <span class="hljs-keyword">await</span> app.platformatic.entities.user.insert({
        <span class="hljs-attr">fields</span>: [<span class="hljs-string">'id'</span>, <span class="hljs-string">'fullName'</span>, <span class="hljs-string">'username'</span>, <span class="hljs-string">'lang'</span>],
        <span class="hljs-attr">inputs</span>: [
          {
            <span class="hljs-attr">id</span>: userId,
            chatId,
            fullName,
            <span class="hljs-attr">username</span>: ctx.update.message.from.username || userId,
            <span class="hljs-attr">lang</span>: ctx.update.message.from.language_code
          }
        ]
      })

      ctx.user = newUser
    }

    <span class="hljs-keyword">return</span> next()
  })

  app.bot.on(<span class="hljs-string">'text'</span>, <span class="hljs-keyword">async</span> (ctx) =&gt; {
    <span class="hljs-keyword">await</span> ctx.reply(<span class="hljs-string">`Hello <span class="hljs-subst">${ctx.user.fullName}</span>!`</span>)
  })
}
</code></pre>
<p>Let's analyze the plugin logic:</p>
<ol>
<li>We don't register any new routes, but we use the <code>app.bot</code> decorator to implement the business logic.</li>
<li>We use the <code>app.bot.use</code> method to register a middleware that will be executed for every message received from Telegram. This middleware will extract the <code>userId</code> and the <code>chatId</code> from the update and will search for the user in the database.</li>
<li>If the user exists, we will set the <code>ctx.user</code> property to the user object, enhancing the Telegraf's <code>ctx</code> object with the user information.</li>
<li>If the user doesn't exist, we will create a new user in the database and set the <code>ctx.user</code> property to the new user object.</li>
<li>Finally, we register another listener for the <code>text</code> event of the <code>app.bot</code> and we use the <code>ctx.user</code> property to customize the reply message.</li>
</ol>
<p>With this logic in place, you can start the server with <code>npm start</code>. When you open a chat with your bot, you'll receive a personalized <code>Hello {name}!</code> message.</p>
<h2 id="heading-summary">Summary</h2>
<p>Congratulations! 🎉 You've just embarked on an exciting journey to create your very own Telegram bot from scratch.
In this article, we've harnessed the power of <a target="_blank" href="https://platformatic.dev/">Platformatic</a> to expedite our project setup, establishing a solid foundation for our bot.<br />We've also delved into the process of generating a basic Telegram bot that responds to user messages and interacts with the database to register users.<br />Now the possibilities are endless, and your Telegram bot can be customized to suit your unique needs.  </p>
<p>I build two complete Telegram bots with Platformatic if you want to see more examples:</p>
<ul>
<li><a target="_blank" href="https://github.com/Eomm/telegram-bot-questions"><code>telegram-bot-questions</code></a>: a bot to ask questions to your team mates. It integrates with Google Sheets to store the answers and has a step by step guide to implement the bot.</li>
<li><a target="_blank" href="https://github.com/Eomm/telegram-bot-give-away"><code>telegram-bot-give-away</code></a>: a bot to organize a custom give away.</li>
</ul>
<p>If you want to learn how to deploy your Telegram bot to the cloud, check out the <a target="_blank" href="https://platformatic.dev/cloud">Platformatic Cloud</a> documentation or leave a comment below and I'll write a new article about it!</p>
<p>The world of chatbot development is at your fingertips, and with each step, you're one step closer to mastering it. Happy coding! 🤖💬</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Fastify has a new Application Hook!]]></title><description><![CDATA[Fastify v4.23.0 has just been released, featuring a brand-new onListen Application hook. In this article, we'll explore what it is and how you can leverage it in your Fastify applications.
Understanding Fastify hooks
Fastify's hooks are a fundamental...]]></description><link>https://backend.cafe/fastify-has-a-new-application-hook</link><guid isPermaLink="true">https://backend.cafe/fastify-has-a-new-application-hook</guid><category><![CDATA[backend]]></category><category><![CDATA[fastify]]></category><category><![CDATA[hooks]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Mon, 18 Sep 2023 06:50:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Rs5BQj5zbf8/upload/51f7e7e50d1d75c2fa1308d297eefd99.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Fastify <a target="_blank" href="https://github.com/fastify/fastify/releases/tag/v4.23.0">v4.23.0</a> has just been released, featuring a brand-new <a target="_blank" href="https://github.com/fastify/fastify/pull/4899"><code>onListen</code></a> Application hook. In this article, we'll explore what it is and how you can leverage it in your Fastify applications.</p>
<h2 id="heading-understanding-fastify-hooks">Understanding Fastify hooks</h2>
<p>Fastify's hooks are a fundamental aspect of its design. Hooks are functions that get called at specific points in a component's lifecycle. They allow you to inject custom functionality at these crucial moments, thus altering the component's behavior.</p>
<p>Hooks are particularly useful for breaking down your application logic into reusable pieces that can be executed at specific times.</p>
<p>Fastify offers two primary types of hooks:</p>
<ul>
<li><strong>Application hooks</strong>: These consist of 5 hooks that are invoked in a specific order during the application's lifecycle.</li>
<li><strong>Request/Reply hooks</strong>: There are a total of 10 hooks that are called in a specific order during the <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Lifecycle">HTTP request lifecycle</a>.</li>
</ul>
<p>Each hook type serves a distinct purpose and comes with its own API.
Before diving into the new <code>onListen</code> hook, let's briefly overview Fastify's hooks.</p>
<h2 id="heading-application-hooks">Application hooks</h2>
<p>Application hooks allow you to customize how your Fastify application initializes and closes.
These hooks play a crucial role in starting and stopping the application gracefully.</p>
<p>🛫 The application hooks during application startup include:</p>
<ul>
<li><code>onRoute</code>: Triggered when a route is registered.</li>
<li><code>onRegister</code>: Invoked when an encapsulated plugin is registered.</li>
<li><code>onReady</code>: Called when the application is ready but not yet listening to incoming requests.</li>
</ul>
<p>If any of these hooks throw an error, the application won't start. These hooks are essential for performing mandatory checks before launching the application, and they execute in the order they are registered.</p>
<p>🛬 The application hooks during application shutdown are:</p>
<ul>
<li><code>preClose</code>: Executed before the application is closed, while HTTP requests are still being processed.</li>
<li><code>onClose</code>: Triggered when the application no longer accepts new requests.</li>
</ul>
<p>These hooks are handy for performing cleanup tasks before shutting down the application, such as closing a database connection. However, they behave differently when an error is thrown:</p>
<ul>
<li><code>preClose</code> hooks are executed in the order they are registered, and if one of them throws an error, subsequent hook functions are not executed. It is likely that you will not use this hook in your application.</li>
<li><code>onClose</code> hooks are executed in the reverse order they are registered, and if one of them throws an error, the following hook functions are executed regardless.</li>
</ul>
<p>You can find more details about these hooks in my <a target="_blank" href="https://backend.cafe/the-fastify-book-is-out">Fastify book 📙</a>!</p>
<h3 id="heading-the-new-onlisten-hook">The new <code>onListen</code> hook</h3>
<p>With the release of Fastify <a target="_blank" href="https://github.com/fastify/fastify/releases/tag/v4.23.0">v4.23.0</a>, we introduce the sixth application hook: <code>onListen</code>.
This hook is called when the application is ready, and the server is actively listening to incoming requests.</p>
<p>The API for the <code>onListen</code> hook is consistent with other application hooks, supporting both async/await and callback styles:</p>
<pre><code class="lang-js"><span class="hljs-comment">// async/await style</span>
app.addHook(<span class="hljs-string">'onListen'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Some async code</span>
})

<span class="hljs-comment">// or callback style</span>
app.addHook(<span class="hljs-string">'onListen'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">done</span>) </span>{
  <span class="hljs-comment">// Some code</span>
  <span class="hljs-keyword">const</span> err = <span class="hljs-literal">null</span>;
  done(err)
})
</code></pre>
<p>In the application startup sequence, you can now list this new hook as the final step:</p>
<ul>
<li><code>onRoute</code></li>
<li><code>onRegister</code></li>
<li><code>onReady</code></li>
<li>⭐️ <code>onListen</code>: Invoked when the application is ready, and the server is actively handling incoming requests.</li>
</ul>
<p>It's essential to note that this hook has some differences compared to the others of the same type:</p>
<ol>
<li>If the <code>onListen</code> hook throws an error, the application will still start anyway, similar to the behavior of the <code>onClose</code> hook.</li>
<li>Since the server is already processing HTTP requests, be cautious of potential race conditions if you attempt to load data needed by your routes.</li>
<li>If you don't call <code>app.listen()</code> in your tests, this hook will not be executed.</li>
</ol>
<p>So, when should you use the <code>onListen</code> hook?</p>
<p>This hook was added in response to community requests to perform tasks that require the server to be actively listening to incoming requests. A common use case is loading external data from a remote service to populate the application cache.</p>
<p>The power of the <code>onListen</code> hook lies in its ability to load data asynchronously without blocking or delaying the application startup. It's perfect for loading non-mandatory data for your routes. If you need to load data that's essential for a decorator and must be available before the application starts, consider using the <code>onReady</code> hook instead.</p>
<h2 id="heading-requestreply-hooks">Request/Reply hooks</h2>
<p>While we won't delve into Request/Reply hooks in this article, you can find comprehensive information in the <a target="_blank" href="https://fastify.dev/docs/latest/Reference/Hooks/#requestreply-hooks">Fastify documentation</a> or in chapter 4 of the detailed <a target="_blank" href="https://backend.cafe/the-fastify-book-is-out">Fastify book 📙</a>!</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we've introduced the new <code>onListen</code> hook, explained how it works and highlighted its use cases. Fastify continues to evolve in response to the community's needs, and this hook is a prime example of that.</p>
<p>If you found this article helpful, please leave a comment, share it with others, and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a> for more updates!</p>
]]></content:encoded></item><item><title><![CDATA[Streaming PostgreSQL data with Fastify]]></title><description><![CDATA[In recent years, we have been surrounded by cool, interactive data visualizations.
These visualizations are powered by datasets stored in databases and made available through APIs.
Clients can then query the API and get the data they need to render t...]]></description><link>https://backend.cafe/streaming-postgresql-data-with-fastify</link><guid isPermaLink="true">https://backend.cafe/streaming-postgresql-data-with-fastify</guid><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[fastify]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Tue, 22 Aug 2023 07:20:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/__0Yg6-lvP8/upload/d44c8914ff14da53faeea68db9a9298a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In recent years, we have been surrounded by cool, interactive data visualizations.
These visualizations are powered by datasets stored in databases and made available through APIs.</p>
<p>Clients can then query the API and get the data they need to render the visualization.</p>
<p>But what happens when the dataset is enormous?
And what if the query to access the data is <strong>complex</strong> and <strong>slow</strong>?</p>
<p>You may be used to adding a 'cool' loading animation to your visualization and waiting for the data to be loaded
but this is not the best user experience.</p>
<p>So, we will see how to stream a massive amount of data from a PostgreSQL database to a Reactjs client!</p>
<h2 id="heading-build-the-dataset">Build the dataset</h2>
<p>I will go fast here because this is not the main topic of this article, and the code is available on <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/">GitHub</a>.</p>
<p>We will create two tables that represent our huge dataset:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Table</td><td>Rows</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>desks</code></td><td>200,000</td><td>200 thousand desks in a building</td></tr>
<tr>
<td><code>items</code></td><td>10,000,000</td><td>10 million items on the desks</td></tr>
</tbody>
</table>
</div><p>The query we are going to analyze is the following:</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">WITH</span> start_time <span class="hljs-keyword">AS</span> (<span class="hljs-keyword">SELECT</span> pg_sleep(<span class="hljs-number">1</span>) <span class="hljs-keyword">AS</span> start_time)
  <span class="hljs-keyword">SELECT</span> items.id, desks.name, row_number() <span class="hljs-keyword">OVER</span> (<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> items.id) <span class="hljs-keyword">AS</span> row_number
  <span class="hljs-keyword">FROM</span> items
  <span class="hljs-keyword">INNER</span> <span class="hljs-keyword">JOIN</span> desks <span class="hljs-keyword">ON</span> desks.id = items.desk_id
  <span class="hljs-keyword">CROSS</span> <span class="hljs-keyword">JOIN</span> start_time
</code></pre>
<p>It reads all the items and the desks they are on and returns the result with a row number.</p>
<p>Since the query is not too slow, we need to slow it down to simulate a real-world scenario with multiple joins and aggregations.
The <code>pg_sleep(1)</code> forces PostgreSQL to wait for 1 second before returning the result.</p>
<p>Now we can start the PostgreSQL server with a quick Docker command:</p>
<pre><code class="lang-bash">docker run --rm -p 5432:5432 --name fastify-postgres -e POSTGRES_PASSWORD=postgres -d postgres:15-alpine
</code></pre>
<p>Finally, we can seed the database with the tables and the data with the <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/"><code>node seed.js</code> script on GitHub</a>.
It will take a few minutes to complete, but we will have a huge dataset to play with at the end!</p>
<h2 id="heading-build-the-api">Build the API</h2>
<p>We are going to use <a target="_blank" href="https://www.fastify.dev/">Fastify</a> to build the API,
it has everything we need to build a fast and scalable API in no time 🎉.</p>
<p>But, before writing the code, we need to think about the API we want to build.
Since we want to return a huge amount of data, we can't run the query and return the result in one shot: whatever server we use, it will run out of memory!</p>
<p>So we need to stream the result of the query to the client by using different techniques:</p>
<ul>
<li><strong>Batching</strong>: we can run the query in batches and return the result in chunks.</li>
<li><strong>Streaming</strong>: we can stream the result of the query to the client as soon as we get the data.</li>
</ul>
<p>The Batching approach can be implemented using the SQL language's <code>LIMIT</code> and <code>OFFSET</code> clauses.
So we don't need any external library to implement it.</p>
<p>The Streaming approach is more complex, but it is more efficient and allows us to return the data as soon as we get it.
A quick Google search will show you that a few libraries can help us with this task:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/pg-cursor"><code>pg-cursor</code></a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/pg-query-stream"><code>pg-query-stream</code></a></li>
</ul>
<p>Both libraries are great, but the <code>pg-query-stream</code> has a better API, so we will use it.</p>
<blockquote>
<p><strong>Note</strong><br />I tested both <code>pg-</code> libraries before noticing that <code>pg-query-stream</code> uses <code>pg-cursor</code> under the hood 😅
The performance of the two libraries is the same, so we will discuss <code>pg-query-stream</code> only.</p>
</blockquote>
<p>Now that we know what to do, we can start writing the code!</p>
<h3 id="heading-project-setup">Project setup</h3>
<p>We will create a quick project setup.
It is important to note that the code is not optimized for production use. This is to keep it as simple as possible.</p>
<p>If you want to learn more about Fastify, you may find the <a target="_blank" href="https://backend.cafe/fastify-v4-book">Fastify book</a> helpful!</p>
<p>So, let's start creating a new project from scratch with the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create the project</span>
mkdir fastify-postgres-high-volume
<span class="hljs-built_in">cd</span> fastify-postgres-high-volume
npm init -y

<span class="hljs-comment"># Install Fastify</span>
npm install fastify @fastify/postgres

<span class="hljs-comment"># Install the PostgreSQL driver and utilities</span>
npm install pg pg-query-stream JSONStream
</code></pre>
<p>Create the <code>app.js</code> file with the following content:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>).promises

<span class="hljs-comment">// Create the Fastify server</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)({ <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span> })

<span class="hljs-comment">// A simple route to serve the Reactjs application</span>
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">serveUi</span> (<span class="hljs-params">request, reply</span>) </span>{
  reply.type(<span class="hljs-string">'text/html'</span>)
  <span class="hljs-keyword">return</span> fs.readFile(<span class="hljs-string">'./index.html'</span>)
})

<span class="hljs-comment">// Register the PostgreSQL plugin to connect to the database</span>
app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'@fastify/postgres'</span>), {
  <span class="hljs-attr">host</span>: <span class="hljs-string">'localhost'</span>,
  <span class="hljs-attr">port</span>: <span class="hljs-number">5432</span>,
  <span class="hljs-attr">database</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">user</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">password</span>: <span class="hljs-string">'postgres'</span>,
  <span class="hljs-attr">max</span>: <span class="hljs-number">20</span>
})

<span class="hljs-comment">// Register some plugins to implement the API - we will see them later</span>
app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'./lib/batch'</span>))
app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'./lib/stream'</span>))

<span class="hljs-comment">// Start the server</span>
app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">8080</span>, <span class="hljs-attr">host</span>: <span class="hljs-string">'0.0.0.0'</span> })
</code></pre>
<p>It is time to scaffold the <code>lib/batch.js</code> plugin:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app, opts</span>) </span>{
  app.get(<span class="hljs-string">'/api/batch'</span>, queryBatch)
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queryBatch</span> (<span class="hljs-params">request, reply</span>) </span>{
  <span class="hljs-keyword">const</span> notImplemented = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Not implemented'</span>)
  notImplemented.statusCode = <span class="hljs-number">501</span>
  <span class="hljs-keyword">throw</span> notImplemented
}
</code></pre>
<p>Let's scaffold the <code>lib/stream.js</code> plugin too:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app, opts</span>) </span>{
  app.get(<span class="hljs-string">'/api/stream'</span>, queryStream)
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queryStream</span> (<span class="hljs-params">request, reply</span>) </span>{
  <span class="hljs-keyword">const</span> notImplemented = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Not implemented'</span>)
  notImplemented.statusCode = <span class="hljs-number">501</span>
  <span class="hljs-keyword">throw</span> notImplemented
}
</code></pre>
<p>We are going to implement the two plugins in the next sections.</p>
<p>The last thing we need to do is to create the <code>index.html</code> file that will be served by the server.
I have created a simple Reactjs application in a single HTML file. You can find it on <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/index.html">GitHub</a>.
The code is not important for this article, so I will not discuss it here; moreover, we need the API to be up and running to test the client, so we will explore it later.</p>
<p>At this point, we can start the server with the following command:</p>
<pre><code class="lang-bash">node app.js
</code></pre>
<blockquote>
<p>💡 Node.js Tip<br />If you are running Node.js 20 or newer, you can use the <code>node --watch app.js</code> flag to reload the server when the code changes!
It doesn't require any external library and is very useful during development.</p>
</blockquote>
<p>Once the server is up and running, we can open the browser and navigate to <code>http://localhost:8080</code> and
we should see the <code>index.html</code> application!</p>
<p>We can now implement the API's business logic!</p>
<h3 id="heading-implementing-the-batching-approach">Implementing the Batching approach</h3>
<p>The Batching approach is the most common one to implement since it is used broadly in the industry to paginate the results of a query.
The idea is to run the query in batches and return the data chunks, so we need two inputs from the client:</p>
<ul>
<li><code>limit</code>: the number of rows to return in each chunk.</li>
<li><code>offset</code>: the number of rows to skip before returning the batch result.</li>
</ul>
<p>We can get these two values in the <code>request.query</code> object by adding a JSON schema to the route that validates the input:</p>
<pre><code class="lang-js">app.get(<span class="hljs-string">'/api/batch'</span>, {
  <span class="hljs-attr">schema</span>: {
    <span class="hljs-attr">query</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">'object'</span>,
      <span class="hljs-attr">properties</span>: {
        <span class="hljs-attr">offset</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">'number'</span>, <span class="hljs-attr">default</span>: <span class="hljs-number">0</span> },
        <span class="hljs-attr">limit</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">'number'</span>, <span class="hljs-attr">default</span>: <span class="hljs-number">50</span>_000 }
      }
    }
  }
}, queryBatch)
</code></pre>
<p>The <code>queryBatch</code> implementation will look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queryBatch</span> (<span class="hljs-params">request, reply</span>) </span>{
  <span class="hljs-comment">// Get the input from the client</span>
  <span class="hljs-keyword">const</span> offset = request.query.offset
  <span class="hljs-keyword">const</span> batchSize = request.query.limit

  <span class="hljs-comment">// The slow query we want to run</span>
  <span class="hljs-keyword">const</span> slowQuery = <span class="hljs-string">`
    WITH start_time AS (SELECT pg_sleep(1) AS start_time)
    SELECT items.id, desks.name, row_number() OVER (ORDER BY items.id) AS row_number
    FROM items
    INNER JOIN desks ON desks.id = items.desk_id
    CROSS JOIN start_time

    OFFSET $1
    LIMIT $2;
  `</span>

  <span class="hljs-comment">// Run the query and return the result</span>
  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.pg.query(slowQuery, [offset, batchSize])
  <span class="hljs-keyword">return</span> result.rows
}
</code></pre>
<p>When the client calls the <code>/api/batch</code> route, the server will run the <code>slowQuery</code> and return the result.</p>
<p>The <code>this.pg</code> object is an Application Decorator injected by the <code>@fastify/postgres</code> plugin, and it is a PostgreSQL pool!</p>
<p>We can now test the API by running the following command:</p>
<pre><code class="lang-bash">curl http://localhost:8080/api/batch?offset=0&amp;<span class="hljs-built_in">limit</span>=10000
</code></pre>
<p>The server should return the first 10,000 rows of the result of the <code>slowQuery</code>!
Note that the response will pop up in the terminal after 1 second at the latest 🐌.</p>
<h3 id="heading-implementing-the-streaming-approach">Implementing the Streaming approach</h3>
<p>The Streaming approach <strong>could</strong> be more complex to implement. However, thanks to the <a target="_blank" href="https://www.npmjs.com/package/pg-query-stream"><code>pg-query-stream</code></a> library, it is not ✨.</p>
<p>The idea is to stream the result of the query to the client as soon as we get the data.
So we need to create a stream that reads the result of the query and pipe it to the client.
The input we need from the client is the <code>limit</code> only because we are going to stream the result and don't need to skip anything.</p>
<p>We can read the <code>limit</code> input from the <code>request.query</code> object by adding a JSON schema to the route that validates the input:</p>
<pre><code class="lang-js">app.get(<span class="hljs-string">'/api/stream'</span>, {
  <span class="hljs-attr">schema</span>: {
    <span class="hljs-attr">query</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">'object'</span>,
      <span class="hljs-attr">properties</span>: {
        <span class="hljs-attr">limit</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">'number'</span>, <span class="hljs-attr">default</span>: <span class="hljs-number">50</span>_000 }
      }
    }
  }
}, queryStream)
</code></pre>
<p>Now we can implement the <code>queryStream</code> function:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> QueryStream = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg-query-stream'</span>)
<span class="hljs-keyword">const</span> JSONStream = <span class="hljs-built_in">require</span>(<span class="hljs-string">'JSONStream'</span>)

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queryStream</span> (<span class="hljs-params">request, reply</span>) </span>{
  <span class="hljs-comment">// Get the PostgreSQL client from the pool</span>
  <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.pg.connect()

  <span class="hljs-comment">// The slow query we want to run</span>
  <span class="hljs-keyword">const</span> slowQuery = <span class="hljs-string">`
    WITH start_time AS (SELECT pg_sleep(1) AS start_time)
    SELECT items.id, desks.name, row_number() OVER (ORDER BY items.id) AS row_number
    FROM items
    INNER JOIN desks ON desks.id = items.desk_id
    CROSS JOIN start_time

    LIMIT $1;
  `</span>

  <span class="hljs-comment">// Create a new stream that runs the query</span>
  <span class="hljs-keyword">const</span> query = <span class="hljs-keyword">new</span> QueryStream(slowQuery, [request.query.limit], {
    <span class="hljs-attr">highWaterMark</span>: <span class="hljs-number">500</span>
  })

  <span class="hljs-comment">// Run the query and return the stream</span>
  <span class="hljs-keyword">const</span> stream = client.query(query)
  stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> { client.release() })
  <span class="hljs-keyword">return</span> stream.pipe(JSONStream.stringify())
}
</code></pre>
<p>Let's analyze the code:</p>
<ol>
<li>We create a new PostgreSQL client by calling <code>this.pg.connect()</code> that returns an available <code>client</code> from the pool.</li>
<li>We create a new <code>QueryStream</code> that will run the same <code>slowQuery</code> as the batch implementation except for the <code>OFFSET</code> clause:<ul>
<li>It is important to note that we need to set the <code>highWaterMark</code> option to read a good amount of rows from the database. The <a target="_blank" href="https://nodejs.org/api/stream.html#streamgetdefaulthighwatermarkobjectmode">default value is 16</a>, which is too low for our use case!</li>
</ul>
</li>
<li>We create a new <code>stream</code> by calling <code>client.query(query)</code> that will execute the query.</li>
<li>We must remember to listen for the <code>end</code> event of the <code>stream</code> to release the <code>client</code> back to the pool.</li>
<li>We return the <code>stream</code> piped to a <code>JSONStream</code> that will convert the result to a JSON string. Fastify will automatically manage the stream and send the result to the client!</li>
</ol>
<p>We can now test the API by running the following command:</p>
<pre><code class="lang-bash">curl http://localhost:8080/api/stream?<span class="hljs-built_in">limit</span>=10000
</code></pre>
<p>The server should return the first 10,000 rows of the result of the <code>slowQuery</code>!</p>
<p>You should see the result being streamed to the terminal as soon as the server gets the data from the database!
There is no delay between the rows, so the client can start to render the data as soon as it gets it!
The pop-up effect is gone! 🚀</p>
<h2 id="heading-build-the-client">Build the client</h2>
<p>Now that we have the API up and running, we can build the client to test the API and see the effect of the two approaches.
Since I like minimalism 🤣 and don't want to spend too much time on the client, I will use the <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/index.html">Reactjs application</a> I have created before.</p>
<p>It is a simple application with the following features:</p>
<ul>
<li>An input to set globally for the application how many rows we must fetch from the API calls.</li>
<li>A button to call the <code>/api/batch</code> route and show the time it takes to get the result.<ul>
<li>An input to set the maximum number of rows to get for each batch: we can't get all the rows in one shot, or the server will run out of memory!</li>
</ul>
</li>
<li>A button to call the <code>/api/stream</code> route and show the time it takes to get the result.</li>
</ul>
<p>Here is a screenshot of the application:</p>
<p><img src="https://github.com/Eomm/fastify-discord-bot-demo/raw/8a1272454722a54bf3b62764d2ea3ccab54daabb/posts/assets/pg-idle.png" alt="the application UI" /></p>
<p>Now, let's press the buttons and see what happens!</p>
<h2 id="heading-results">Results</h2>
<p>If we press the buttons with the default values, we will get the following results:</p>
<p><img src="https://github.com/Eomm/fastify-discord-bot-demo/raw/8a1272454722a54bf3b62764d2ea3ccab54daabb/posts/assets/pg-results.png" alt="the application UI results" /></p>
<p>It must be noted that the results include the time it takes to parse the data in the browser.</p>
<p>Before analyzing the results, let's try to play with the inputs to see how the results change.</p>
<p>What happens if we increase the batch size to 350,000?</p>
<p><img src="https://github.com/Eomm/fastify-discord-bot-demo/raw/8a1272454722a54bf3b62764d2ea3ccab54daabb/posts/assets/pg-big-batch.png" alt="the application UI results" /></p>
<p>The batch approach is much faster than before, and it beats the streaming method!</p>
<p>Let's analyze the results:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Total Rows</td><td>Batch Size</td><td>Batch Result Time (sec)</td><td>Stream Result Time (sec)</td><td>Number of Batches</td></tr>
</thead>
<tbody>
<tr>
<td>500,000</td><td>50,000</td><td>~14</td><td>~7 🥇</td><td>10</td></tr>
<tr>
<td>500,000</td><td>350,000</td><td>~5 🥇</td><td>~7</td><td>2</td></tr>
<tr>
<td>1,000,000</td><td>350,000</td><td>~8 🥇</td><td>~15</td><td>3</td></tr>
<tr>
<td>3,000,000</td><td>350,000</td><td>~30 🥈</td><td>~30 🥈</td><td>9</td></tr>
<tr>
<td>3,500,000</td><td>350,000</td><td>~36</td><td>~34 🥇</td><td>10</td></tr>
<tr>
<td>5,000,000</td><td>350,000</td><td>~63</td><td>~53 🥇</td><td>15</td></tr>
</tbody>
</table>
</div><blockquote>
<p>Each test result is the best of 3 tests after refreshing the web page and clearing the cache.
The Fastify server runs locally on a MacBook Pro 2019 and is never restarted during the tests.</p>
</blockquote>
<p>The results are exciting!
The streaming approach is faster than the batch approach when the number of batches (calculated by <code>totalRows / batchSize</code>) is higher or equal to 10.</p>
<p>Is this a general rule? Let's answer this question by doing some investigating!</p>
<p>Watch this video taken from the Chrome DevTools's Network tab about the 2nd test result:</p>
<p><img src="https://github.com/Eomm/fastify-discord-bot-demo/raw/8a1272454722a54bf3b62764d2ea3ccab54daabb/posts/assets/pg-network.gif" alt="the application UI results" /></p>
<p>We can see that the batch approach makes two requests to the server, while the streaming approach makes only one request.
But the <code>Content Download</code> time is much higher for the streaming approach.</p>
<p>Could we improve it?
Yes, of course!! Let's try to increase the <code>highWaterMark</code> option of the <code>QueryStream</code> to 5MB!</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> query = <span class="hljs-keyword">new</span> QueryStream(slowQuery, [request.query.limit], {
  <span class="hljs-attr">highWaterMark</span>: <span class="hljs-number">5e6</span>
})
</code></pre>
<p>Let's rerun the tests, and you should see that the total time takes ~4/5 seconds for the streaming approach (body parsing included)!
The <code>Content Download</code> time is now ~2 seconds, so by increasing the <code>highWaterMark</code>, we speed up the download of the data <strong>x2</strong>!</p>
<p>Now, if we rerun the 3rd test with the new <code>highWaterMark</code>, the streaming approach ends in ~10 seconds, so we cut down the time by 1/3!</p>
<p>This means that the 10 batches rule is not a general rule to defeat the streaming approach, but it depends on how we configure our stream.</p>
<p>So the speed relation of the two approaches is the following:</p>
<ul>
<li><p><strong>Batching</strong>: If we have a small number of batches, the batch approach is faster to implement and run. But we can't increase the batch size too much to reduce the number of batches. The 350,000 rows batch size fetched ~30 MB of data; that's way too much for a single set but was helpful for our understanding.</p>
</li>
<li><p><strong>Streaming</strong>: It is not always the fastest approach if we don't configure the stream properly. However, it is the most efficient approach because it doesn't require loading all the data in memory before sending it to the client. Moreover, it is the more stable approach in the long run because it doesn't require changing the batch size when the dataset grows, and it lets us build more responsive UI applications.</p>
</li>
</ul>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we have seen how to stream a massive amount of data from a PostgreSQL database to a Reactjs client!
We explored two different approaches, and we have seen how to implement them with Fastify and the <code>pg-query-stream</code> library.</p>
<p>The performance of the two approaches has been compared, and we have seen that the streaming approach is the most efficient and stable in the long run — even if it is not always the fastest.</p>
<p>We analyzed the results and found a relation between each approach and its performance.
Now you can choose the best approach for your use case by knowing the pros and cons of each approach!</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[The Fastify book is out!]]></title><description><![CDATA[Are you a mid/expert back-end engineer looking to build highly scalable and maintainable API servers? Are you already familiar with Node.js and other back-end frameworks? If so, then "Accelerating Server-Side Development with Fastify" is the book you...]]></description><link>https://backend.cafe/the-fastify-book-is-out</link><guid isPermaLink="true">https://backend.cafe/the-fastify-book-is-out</guid><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Express]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[fastify]]></category><dc:creator><![CDATA[Maksim Sinik]]></dc:creator><pubDate>Sun, 30 Jul 2023 14:42:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690722106618/03dab311-2651-4e5f-9266-2b02c84eefc6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Are you a mid/expert back-end engineer looking to build highly scalable and maintainable API servers? Are you already familiar with Node.js and other back-end frameworks? If so, then "Accelerating Server-Side Development with Fastify" is the book you've been waiting for!</p>
<p><strong>About the Authors:</strong> Meet the team behind this comprehensive guide - Manuel Spigolon, a Senior Backend Developer at NeaForm, with expertise in Backend Software Development for IT Services; Maksim Sinik, a Senior Engineering Manager at TrustLayer Inc. in the Insurtech, Healthcare, and Logistics industries, specializing in Node.js Backend Scalability; and Matteo Collina, Co-Founder &amp; CTO of Platformatic, a renowned Open Source author and core maintainer of Fastify.</p>
<p><strong>Who is this Book For?</strong> This book targets mid/expert back-end engineers with prior experience in Node.js and other back-end frameworks. It assumes you are familiar with JavaScript and HTTP protocol but are looking to understand the unique selling points of Fastify. Whether you are developing a monolithic application or planning to transition to microservices, this guide will show you how to leverage Fastify's features for optimal results.</p>
<p><strong>Why Choose "Accelerating Server-Side Development with Fastify"?</strong> While there are other books on Fastify, they often touch on various aspects of development without diving deep into creating scalable and maintainable applications. This book focuses solely on Fastify and delves into its core logic and features, helping you avoid common pitfalls and build highly responsive applications.</p>
<p><strong>Learning Outcomes</strong> Throughout this book, you'll learn the following essential skills:</p>
<ol>
<li><p>Understand encapsulation techniques implemented by Fastify.</p>
</li>
<li><p>Become proficient in using Fastify's unique features compared to other frameworks.</p>
</li>
<li><p>Learn to organize project structures and implement architectures for transitioning to microservices.</p>
</li>
<li><p>Develop a real-world project to put theory into practice.</p>
</li>
<li><p>Deploy, monitor, and handle errors in a running Fastify instance effectively.</p>
</li>
</ol>
<p><strong>Book Structure</strong> The book is divided into three parts, each addressing different aspects of Fastify development:</p>
<p><strong>Part One: Fastify Basics</strong></p>
<ol>
<li><p>What is Fastify?</p>
</li>
<li><p>The Plugin System and the Boot Process</p>
</li>
<li><p>Working with Routes</p>
</li>
<li><p>Exploring Hooks</p>
</li>
<li><p>Exploring Validation and Serialization</p>
</li>
</ol>
<p><strong>Part Two: Building a Real-World Project</strong></p>
<ol>
<li><p>Project Structure and Configuration Management</p>
</li>
<li><p>Building a RESTful API</p>
</li>
<li><p>Authentication, Authorization, and Files Handling</p>
</li>
<li><p>Application Testing</p>
</li>
<li><p>Deployment and Process Monitoring for a Healthy Application</p>
</li>
<li><p>Meaningful Application Logging</p>
</li>
</ol>
<p><strong>Part Three: Advanced Topics</strong></p>
<ol>
<li><p>From a Monolith to Microservices</p>
</li>
<li><p>Performance Assessment and Improvement</p>
</li>
<li><p>Developing a GraphQL API</p>
</li>
<li><p>Type-Safe Fastify</p>
</li>
</ol>
<p>If you're ready to accelerate your server-side development with Fastify, this comprehensive guide is a must-have for your toolkit!</p>
<p>For more updates and technical discussions, follow the authors on Twitter:</p>
<ul>
<li><p>Maksim Sinik: <a target="_blank" href="https://hashnode.com/@fox1t">@maksimsinik</a></p>
</li>
<li><p>Manuel Spigolon: <a target="_blank" href="https://hashnode.com/@Eomm">@manueomm</a></p>
</li>
<li><p>Matteo Collina: @matteocollina</p>
</li>
</ul>
<p>Or subscribe to this blog!</p>
<p><a target="_blank" href="https://www.amazon.com/Accelerating-Server-Side-Development-Fastify-comprehensive/dp/1800563582/">Get your copy of "Accelerating Server-Side Development with Fastify"</a> today and embark on a journey towards building scalable and maintainable backend applications!</p>
]]></content:encoded></item><item><title><![CDATA[Validate the Fastify input with Joi]]></title><description><![CDATA[If you are an hapi developer, you may know the joi library. It is a powerful validation library that allows you to define programmatic schemas for your data and validate them with ease.
One of the hardest things about migrating from hapi to fastify i...]]></description><link>https://backend.cafe/validate-the-fastify-input-with-joi</link><guid isPermaLink="true">https://backend.cafe/validate-the-fastify-input-with-joi</guid><category><![CDATA[fastify]]></category><category><![CDATA[backend]]></category><category><![CDATA[Input Validation]]></category><category><![CDATA[hapi]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Mon, 10 Jul 2023 06:54:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/gM-RfQsZK98/upload/78f700e3ab3ac4daa661759713b84380.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you are an <code>hapi</code> developer, you may know the <a target="_blank" href="https://www.npmjs.com/package/joi"><code>joi</code></a> library. It is a powerful validation library that allows you to define programmatic schemas for your data and validate them with ease.</p>
<p>One of the hardest things about migrating from <code>hapi</code> to <code>fastify</code> is the complexity that’s involved for you to be able to keep using <code>joi</code> for the route's input validation.</p>
<p>But now, thanks to the <a target="_blank" href="https://www.npmjs.com/package/joi-compiler"><code>joi-compiler</code></a> library, you can use <code>joi</code> with <code>fastify</code> without any problem!</p>
<blockquote>
<p><strong>Note</strong> If you have been afraid to use it because it has very few downloads, don't worry — it is a new library and I'm the author. And, if you don't know me, I'm a Fastify core maintainer and I'm the co-author of the <a target="_blank" href="https://backend.cafe/fastify-v4-book">Fastify book</a> too!</p>
</blockquote>
<p>The <code>joi-compiler</code> module replaces the default <code>ajv</code> schema compiler with a <code>joi</code>-based one and allows you to use <code>joi</code> schemas to validate your data. Let's see how to use it!</p>
<h2 id="heading-integrate-joi-with-fastify">Integrate <code>joi</code> with <code>fastify</code></h2>
<p>After you have installed the latest <code>fastify@4</code> and <code>joi-compiler</code> module:</p>
<pre><code class="lang-bash">mkdir fastify-joi
<span class="hljs-built_in">cd</span> fastify-joi
npm init -y
npm install --save fastify@4 joi-compiler
</code></pre>
<p>We can create a simple <code>index.js</code> file with the following content:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)
<span class="hljs-keyword">const</span> JoiCompiler = <span class="hljs-built_in">require</span>(<span class="hljs-string">'joi-compiler'</span>)

<span class="hljs-comment">// Instantiate the compiler</span>
<span class="hljs-keyword">const</span> joiCompilerInstance = JoiCompiler()

<span class="hljs-comment">// Install it to Fastify</span>
<span class="hljs-keyword">const</span> app = fastify({
  <span class="hljs-attr">schemaController</span>: {
    <span class="hljs-attr">bucket</span>: joiCompilerInstance.bucket,
    <span class="hljs-attr">compilersFactory</span>: {
      <span class="hljs-attr">buildValidator</span>: joiCompilerInstance.buildValidator
    }
  }
})

<span class="hljs-comment">// Use it!</span>
<span class="hljs-keyword">const</span> joiSchema = joiCompilerInstance.joi.object({
  <span class="hljs-attr">foo</span>: joiCompilerInstance.joi.string().required()
})

app.get(<span class="hljs-string">'/'</span>, {
  <span class="hljs-attr">handler</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">'Hello World!'</span>,
  <span class="hljs-attr">schema</span>: {
    <span class="hljs-attr">headers</span>: joiSchema
  }
})

app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> })
</code></pre>
<p>If you run this code, you will be able to test the <code>joi</code> validation with the following command:</p>
<pre><code class="lang-bash">curl -X GET http://localhost:3000/
<span class="hljs-comment"># {"statusCode":400,"error":"Bad Request","message":"\"foo\" is required"}</span>

curl -X GET http://localhost:3000/ -H <span class="hljs-string">"foo: bar"</span>
<span class="hljs-comment"># Hello World!</span>
</code></pre>
<p>As you can see, the <code>joi</code> schema is working as expected! In a few lines of code, we have integrated <code>joi</code> with <code>fastify</code> and we are able to use it to validate the input of your application routes.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>The <code>joi-compiler</code> module is a Fastify schema compiler, it is used by Fastify to build the components during the startup to guarantee the <a target="_blank" href="https://backend.cafe/the-complete-guide-to-the-fastify-plugin-system">encapsulated feature</a>.</p>
<p>For this reason, the <code>joi-compiler</code> can be configured in the <code>schemaController</code> option during the Fastify application creation.</p>
<blockquote>
<p><strong>Note</strong><br />How the <a target="_blank" href="https://www.fastify.io/docs/latest/Reference/Server/#schemacontroller">Fastify Schema Controller</a> works is out of the scope of this article. But if you want to know more about it, you can read the <a target="_blank" href="https://backend.cafe/fastify-v4-book">Fastify book</a>!</p>
</blockquote>
<h2 id="heading-how-to-configure-the-joi-compiler">How to configure the <code>joi-compiler</code></h2>
<p>The <code>JoiCompiler</code> accepts an optional configuration object to customize the <code>joi</code> instance.</p>
<p>The default configuration is:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> joiCompilerInstance = JoiCompiler({
  <span class="hljs-comment">// optionally: provide all the JOI options you need</span>
  <span class="hljs-comment">// Here is all the possible options: https://joi.dev/api/?v=17.9.1#anyvalidatevalue-options</span>
  <span class="hljs-attr">prefs</span>: {
    <span class="hljs-attr">stripUnknown</span>: <span class="hljs-literal">true</span>
  },

  <span class="hljs-comment">// optionally: an array with custom JOI extensions such as `@joi/date`</span>
  <span class="hljs-attr">extensions</span>: [
  ],

  <span class="hljs-comment">// optionally: if you want to use the async validation. Default: false</span>
  <span class="hljs-attr">asyncValidation</span>: <span class="hljs-literal">false</span>
})
</code></pre>
<p>When you instantiate the <code>joiCompilerInstance</code>, the returned object has the following properties:</p>
<ul>
<li><p><code>buildValidator</code>: The function to pass to the <code>schemaController</code> option of Fastify.</p>
</li>
<li><p><code>bucket</code>: The <code>joi</code> bucket that contains the schemas when you call the <code>app.addSchema(joiSchema)</code> method. You can omit this option if you don't use <code>app.addSchema</code>.</p>
</li>
<li><p><code>joi</code>: A customized <code>joi</code> instance that contains the installed <code>extensions</code>, if there are any. It is a good practice to use this instance to build your schemas.</p>
</li>
</ul>
<h2 id="heading-how-to-use-ajv-and-joi-together">How to use <code>ajv</code> and <code>joi</code> together!</h2>
<p>The power of Fastify is that you can use different schema compilers at the same time! Here is an example of how you can learn from the <a target="_blank" href="https://backend.cafe/fastify-v4-book">Fastify book</a>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> joiCompilerInstance = JoiCompiler()
<span class="hljs-keyword">const</span> app = fastify()

app.post(<span class="hljs-string">'/ajv'</span>, {
  <span class="hljs-attr">handler</span>: <span class="hljs-function">(<span class="hljs-params">request</span>) =&gt;</span> request.body,
  <span class="hljs-attr">schema</span>: {
    <span class="hljs-attr">body</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-string">'object'</span>,
      <span class="hljs-attr">properties</span>: {
        <span class="hljs-attr">toX</span>: { <span class="hljs-attr">const</span>: <span class="hljs-number">42</span> },
        <span class="hljs-attr">toY</span>: { <span class="hljs-attr">const</span>: <span class="hljs-number">50</span> }
      }
    }
  }
})

app.register(<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pluginJoi</span> (<span class="hljs-params">app, opts</span>) </span>{
  <span class="hljs-comment">// Install the joi compiler into this encapsulated context!</span>
  app.setSchemaController({
    <span class="hljs-attr">bucket</span>: joiCompilerInstance.bucket,
    <span class="hljs-attr">compilersFactory</span>: {
      <span class="hljs-attr">buildValidator</span>: joiCompilerInstance.buildValidator
    }
  })

  <span class="hljs-comment">// Let's try to use the external schemas!</span>
  app.addSchema({ <span class="hljs-attr">$id</span>: <span class="hljs-string">'x'</span>, <span class="hljs-attr">$value</span>: <span class="hljs-number">42</span> })
  app.addSchema({ <span class="hljs-attr">$id</span>: <span class="hljs-string">'y'</span>, <span class="hljs-attr">$value</span>: <span class="hljs-number">50</span> })

  app.post(<span class="hljs-string">'/joi'</span>, {
    <span class="hljs-attr">handler</span>: <span class="hljs-function">(<span class="hljs-params">request</span>) =&gt;</span> request.body,
    <span class="hljs-attr">schema</span>: {
      <span class="hljs-attr">body</span>: Joi.object({
        <span class="hljs-attr">toX</span>: Joi.ref(<span class="hljs-string">'$x'</span>),
        <span class="hljs-attr">toY</span>: Joi.ref(<span class="hljs-string">'$y'</span>)
      })
    }
  })
})

app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> })
</code></pre>
<p>If you run this code, you will be able to use the <code>ajv</code> and <code>joi</code> validation in the same application! The <code>ajv</code> validation will be used for the <code>/ajv</code> route and the <code>joi</code> validation will be used for the <code>/joi</code> route.</p>
<p>Pretty cool, right?</p>
<h2 id="heading-summary">Summary</h2>
<p>The <a target="_blank" href="https://www.npmjs.com/package/joi-compiler"><code>joi-compiler</code></a> is a powerful tool that allows you to build and manage <code>joi</code> instances for Fastify out of the box in a few lines of code. If you're a <code>joi</code> user, <code>joi-compiler</code> is definitely worth checking out!</p>
<p>If you enjoyed this article comment, please share and follow <a target="_blank" href="https://twitter.com/ManuEomm">@ManuEomm</a> on Twitter!</p>
]]></content:encoded></item><item><title><![CDATA[Unlock the Power of Runtime Log Level Control]]></title><description><![CDATA[As a backend developer, logging is essential to monitoring and debugging applications.
Traditionally, changing an application's log level required restarting it or modifying the configuration and redeploying it.
However, there are scenarios where cha...]]></description><link>https://backend.cafe/unlock-the-power-of-runtime-log-level-control</link><guid isPermaLink="true">https://backend.cafe/unlock-the-power-of-runtime-log-level-control</guid><category><![CDATA[fastify]]></category><category><![CDATA[backend]]></category><category><![CDATA[logging]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sun, 04 Jun 2023 07:30:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GbAEJUJKJ88/upload/3ff6bd5489df07ae4fac004adedb3178.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a backend developer, logging is essential to monitoring and debugging applications.
Traditionally, changing an application's log level required restarting it or modifying the configuration and redeploying it.</p>
<p>However, there are scenarios where changing the log level at runtime becomes necessary to monitor and troubleshoot applications efficiently.
This article introduces the new <a target="_blank" href="https://github.com/Eomm/fastify-log-controller"><code>fastify-log-controller</code></a> plugin that enables changing the log level of your application at runtime without the need for restarts or resetting the in-memory state!</p>
<h2 id="heading-the-wrong-log-level">The wrong log level</h2>
<p>Fastify is awesome, and it provides a lot of logging features out of the box:</p>
<ul>
<li>One log level for the entire application</li>
<li>One log level for each encapsulated context</li>
<li>One log level for each route</li>
</ul>
<p>This means that you can write this code to customize the log level of your application:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)({
  <span class="hljs-attr">logger</span>: {
    <span class="hljs-attr">level</span>: <span class="hljs-string">'error'</span>, <span class="hljs-comment">// 🔵 application log level</span>
  },
})
fastify.register(somePlugin, {
  <span class="hljs-attr">logLevel</span>: <span class="hljs-string">'info'</span>, <span class="hljs-comment">// 🔵 encapsulated context log level</span>
})
fastify.get(<span class="hljs-string">'/some-route'</span>, {
  <span class="hljs-attr">logLevel</span>: <span class="hljs-string">'debug'</span>, <span class="hljs-comment">// 🔵 route log level</span>
  <span class="hljs-attr">handler</span>: <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
  }
})
</code></pre>
<p>This feature is useful when you want to reduce the noise of the logs or increase the verbosity of a specific context or route.
Although this feature is powerful, changing it at runtime by default is impossible.<br />So, if you don't implement a custom solution, you can't adapt to changing debugging or monitoring requirements without <strong>restarting the application</strong>! And in the worst case, you may need to <strong>redeploy the application</strong> if the log level is defined in the configuration file!</p>
<p>Sometimes, you may need to increase the log level to get more detailed logs for specific contexts or decrease it to reduce the noise. Having the ability to modify the log level dynamically can significantly enhance your application's debugging and monitoring capabilities, helping your support team troubleshoot issues faster 🚀!</p>
<h2 id="heading-how-to-change-the-log-level-at-runtime">How to change the log level at runtime</h2>
<p>The <a target="_blank" href="https://github.com/Eomm/fastify-log-controller"><code>fastify-log-controller</code></a> plugin provides a simple and elegant solution to the problem of changing log levels at runtime.<br />By registering this plugin in your Fastify application, it automatically adds a new route at <code>POST /log-level</code> <em>(configurable)</em> that allows you to control the log levels for different encapsulated contexts!</p>
<p>To use this plugin, you need to follow these steps:</p>
<ol>
<li>Install the plugin and register it in your Fastify application</li>
<li>Assign a unique name to each encapsulated context or route</li>
<li>Call the <code>POST /log-level</code> route to change the log level of a specific unique name</li>
</ol>
<p>Here is the first step:</p>
<pre><code class="lang-bash">npm install fastify fastify-log-controller
</code></pre>
<p>Create a new file, <code>example.js</code>, and add the following scaffold:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)({
    <span class="hljs-attr">disableRequestLogging</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Disable the default request logging to reduce the noise</span>
    <span class="hljs-attr">logger</span>: {
      <span class="hljs-attr">level</span>: <span class="hljs-string">'error'</span>
    }
  })
  <span class="hljs-comment">// Register the plugin</span>
  app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify-log-controller'</span>))
  <span class="hljs-comment">// 📝 ... Define your routes and encapsulated contexts ... 📝</span>
  <span class="hljs-comment">// Check that the route logs the `hello world` message!</span>
  <span class="hljs-keyword">await</span> app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">3000</span> })
}
example()
</code></pre>
<p>Now let's see how to change the log level of a specific encapsulated context.</p>
<h3 id="heading-customize-the-log-level-of-an-encapsulated-context">Customize the log level of an encapsulated context</h3>
<p>To customize the log level of an encapsulated context, you need to assign a unique name to it.
So, modify the <code>example.js</code> file as follows:</p>
<pre><code class="lang-js">  <span class="hljs-comment">// .. app.register(require('fastify-log-controller'))</span>
  <span class="hljs-keyword">const</span> pluginFn = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">plugin</span> (<span class="hljs-params">app, opts</span>) </span>{
    app.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
      request.log.info(<span class="hljs-string">'info message'</span>)
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
    })
  }
  <span class="hljs-keyword">const</span> pluginOpts = {
    <span class="hljs-attr">logCtrl</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">'bar'</span> } <span class="hljs-comment">// ❗️ Set a unique name for the encapsulated context</span>
  }
  <span class="hljs-comment">// Create an encapsulated context with register and set the `logCtrl` option</span>
  app.register(pluginFn, pluginOpts)
  <span class="hljs-comment">// ... await app.listen({ port: 3000 })</span>
</code></pre>
<p>Now, if you run the application with <code>node example.js</code>, you shouldn't see any log message in the console.
Even if you call the <code>/</code> route, the log level is set to <code>error</code>, and the <code>info</code> message is not logged.</p>
<p>To change the log level of the <code>bar</code> encapsulated context, you need to call the <code>POST /log-level</code> route with the following payload:</p>
<pre><code class="lang-bash">curl -X POST \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{"level": "debug", "contextName": "bar"}'</span> \
  http://localhost:3000/log-level
</code></pre>
<p>Now, if you call the <code>/</code> route, you should see the <code>info</code> message in the console!
Since the log level of the <code>bar</code> encapsulated context is set to <code>debug</code>, the <code>info</code> message is logged.</p>
<blockquote>
<p><strong>Note</strong><br />Changing the log level of an encapsulated context will change the log level of all the routes registered in it!  </p>
</blockquote>
<h3 id="heading-customize-the-log-level-of-a-route">Customize the log level of a route</h3>
<p>To customize the log level of a route, you need to do the same thing as for the encapsulated context.
In this case, you will need to wrap the route with the <code>register</code> method and set the <code>logCtrl</code> option as before.</p>
<p>So, modify the <code>example.js</code> file as follows:</p>
<pre><code class="lang-js">  <span class="hljs-comment">// .. app.register(require('fastify-log-controller'))</span>
  <span class="hljs-keyword">const</span> pluginFn = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">plugin</span> (<span class="hljs-params">app, opts</span>) </span>{
    app.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span> (<span class="hljs-params">request, reply</span>) </span>{
      request.log.info(<span class="hljs-string">'info message'</span>)
      <span class="hljs-keyword">return</span> { <span class="hljs-attr">hello</span>: <span class="hljs-string">'world'</span> }
    })
    <span class="hljs-comment">// Wrap the route with register and set the `logCtrl` option as before</span>
    app.register(<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">subPlugin</span> (<span class="hljs-params">app</span>) </span>{
      app.get(<span class="hljs-string">'/route'</span>, {
        <span class="hljs-attr">handler</span>: <span class="hljs-function">(<span class="hljs-params">request, reply</span>) =&gt;</span> {
          request.log.info(<span class="hljs-string">'info message'</span>)
          <span class="hljs-keyword">return</span> {}
        }
      })
    }, { <span class="hljs-attr">logCtrl</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">'foo'</span> } })
  }
  <span class="hljs-comment">// ... const pluginOpts = {</span>
</code></pre>
<p>Now, if you run the application with <code>node example.js</code>, you shouldn't see any log message in the console.
Even if you call the <code>/route</code> URL, the log level is set to <code>error</code>, and the <code>info</code> message is not logged.</p>
<p>To change the log level of the <code>/route</code> route, you need to call the <code>POST /log-level</code> route with the following payload:</p>
<pre><code class="lang-bash">curl -X POST \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{"level": "debug", "contextName": "foo"}'</span> \
  http://localhost:3000/log-level
</code></pre>
<p>Now, if you call the <code>/route</code> route, you should see the <code>info</code> message in the console and the <code>bar</code> encapsulated context is untouched!</p>
<blockquote>
<p><strong>Note</strong><br />If you want more features, like changing the log level of the entire application, you can <a target="_blank" href="https://github.com/Eomm/fastify-log-controller/issues/2">open a feature request</a>!  </p>
</blockquote>
<h3 id="heading-plugin-options">Plugin options</h3>
<p>The <code>fastify-log-controller</code> plugin accepts the following options:</p>
<ul>
<li><code>optionKey: string</code> <em>(default: <code>logCtrl</code>)</em>.<br />The property name used to set the log level of an encapsulated context or route.</li>
<li><code>routeConfig: object</code> <em>(default: <code>{}</code>)</em>.  The object can contain the <a target="_blank" href="https://www.fastify.io/docs/latest/Reference/Routes/#routes-options">Fastify route configuration</a>.
The configuration of the <code>POST /log-level</code> route. You can't change only the <code>handler</code> and <code>schema</code> properties, so you will be able to add an authentication strategy.</li>
</ul>
<h2 id="heading-summary">Summary</h2>
<p>In this article, you have found a convenient solution to the problem of changing log levels at runtime in a Fastify application. By registering this plugin, you can dynamically control the log levels for different encapsulated contexts without needing application restarts or resetting the in-memory state.</p>
<p>This plugin introduces a new route at <code>/log-level</code>, allowing you to send a <code>POST</code> request to modify the log level for a specific encapsulated context. With the ability to adjust log levels at runtime, you can fine-tune your logging strategy, enabling more detailed logs for debugging or reducing noise in production environments.</p>
<p>If you want to go deeper into the encapsulated context concept, check out these sources:</p>
<ul>
<li><a target="_blank" href="https://www.youtube.com/watch?v=BnnL7fAKqNU">YouTube Video</a></li>
<li><a target="_blank" href="https://backend.cafe/the-complete-guide-to-the-fastify-plugin-system">Complete Guide to Fastify plugin system</a></li>
<li><a target="_blank" href="https://stackoverflow.com/questions/61020394/what-is-the-exact-use-of-fastify-plugin/61054534#61054534">What is the exact use of <code>fastify-plugin</code></a></li>
</ul>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Official Release Date of 'Accelerating Server-Side Development with Fastify' Book]]></title><description><![CDATA[Me (Manuel Spigolon), Maksim Sinik and Matteo Collina are thrilled to share some exciting news with the Fastify community!
After two years of hard work and dedication, we are delighted to announce the official release date of our book!
'Accelerating ...]]></description><link>https://backend.cafe/fastify-v4-book</link><guid isPermaLink="true">https://backend.cafe/fastify-v4-book</guid><category><![CDATA[fastify]]></category><category><![CDATA[book]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Announcement]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Mon, 22 May 2023 06:56:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684430241864/80e71d62-c903-4c0d-88d4-13fb7d860112.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Me (<a target="_blank" href="https://twitter.com/ManuEomm">Manuel Spigolon</a>), <a target="_blank" href="https://twitter.com/maksimsinik">Maksim Sinik</a> and <a target="_blank" href="https://twitter.com/matteocollina">Matteo Collina</a> are thrilled to share some exciting news with the Fastify community!
After two years of hard work and dedication, we are delighted to announce the official release date of our book!</p>
<p><a target="_blank" href="https://packt.link/DvIDB"><strong>'Accelerating Server-Side Development with Fastify'</strong></a> will be released on <strong>9th June 2023!</strong> 🗓️</p>
<p>This book aims to be the definitive resource for developers looking to harness the full potential of <a target="_blank" href="https://www.fastify.io/">Fastify</a>, a lightning-fast web framework for Node.js.</p>
<h2 id="heading-build-high-performance-web-applications-with-ease">Build high-performance web applications with ease</h2>
<p><em>'Accelerating Server-Side Development with Fastify'</em> covers many topics.
These include Fastify's core concepts, routing, plugins, testing, and deployment strategies.
Whether you are a seasoned developer or just starting your journey with Fastify, this book will empower you to build high-performance web applications with ease.</p>
<p>What’s inside <em>'Accelerating Server-Side Development with Fastify'</em>:</p>
<ul>
<li>Gain a deep understanding of Fastify's architecture and how it differs from other Node.js frameworks</li>
<li>Explore various routing techniques and advanced features to build robust APIs and microservices</li>
<li>Learn how to leverage Fastify's plugin system to enhance functionality and streamline development</li>
<li>Discover best practices for testing Fastify applications, ensuring code quality and reliability</li>
<li>Master deployment strategies, including containerization and serverless deployment options</li>
</ul>
<h2 id="heading-chapter-list">Chapter list</h2>
<p>Here is a list of the chapters included in the book:</p>
<p><strong>Part 1</strong>: Fastify Basics</p>
<ul>
<li><em>Chapter 1:</em> What Is Fastify?</li>
<li><em>Chapter 2:</em> The Plugin System and the Boot Process</li>
<li><em>Chapter 3:</em> Working with Routes</li>
<li><em>Chapter 4:</em> Exploring Hooks</li>
<li><em>Chapter 5:</em> Exploring Validation and Serialization</li>
</ul>
<p><strong>Part 2</strong>: Build a Real-World Project</p>
<ul>
<li><em>Chapter 6:</em> Project Structure and Configuration Management</li>
<li><em>Chapter 7:</em> Building a RESTful API</li>
<li><em>Chapter 8:</em> Authentication and File Handling</li>
<li><em>Chapter 9:</em> Application Testing</li>
<li><em>Chapter 10:</em> Deployment and Process Monitoring for a Healthy Application</li>
<li><em>Chapter 11:</em> Meaningful Application Logging</li>
</ul>
<p><strong>Part 3</strong>: Advanced Topics</p>
<ul>
<li><em>Chapter 12:</em> From a Monolith to Microservices</li>
<li><em>Chapter 13:</em> Performance Assessment and Improvement</li>
<li><em>Chapter 14:</em> Developing a GraphQL API</li>
<li><em>Chapter 15:</em> Type-Safe Fastify</li>
</ul>
<p>We hope you will find all the content helpful and that it will help you become a Fastify expert!</p>
<h2 id="heading-pre-order-your-copy-today">Pre-order your copy today!</h2>
<p>Pre-order your copy today using the link below and start your journey to becoming a Fastify expert!</p>
<p><a target="_blank" href="https://packt.link/DvIDB">📖 Pre-order the 'Accelerating Server-Side Development with Fastify' book 📘</a></p>
<p>We hope it will serve as a valuable resource for the Fastify community.<br />We look forward to hearing your feedback on the book!</p>
<h2 id="heading-acknowledgments">Acknowledgments</h2>
<p>First and foremost, I would like to express our gratitude to the editor at <a target="_blank" href="https://www.packtpub.com/">Packt</a>,
whose guidance and support were instrumental in shaping this book.
Their expertise and invaluable feedback helped us refine our ideas,
ensuring that the content delivered met the highest standards.
I noticed how much I improved as a writer during the writing process.</p>
<p>I would also like to extend my sincere thanks to <a target="_blank" href="https://twitter.com/maksimsinik">Maksim</a> and <a target="_blank" href="https://twitter.com/matteocollina">Matteo</a>.
Collaborating with such incredible individuals made the process enjoyable and enriched the book's overall quality.
Their insights, technical prowess, and dedication to Fastify made a difference.
Working on this project has been an honour.</p>
<p>Thank you,
Manuel</p>
<p>If you enjoyed this article and that you’ll like <em>'Accelerating Server-Side Development with Fastify'</em>.
Comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[How to handle emojis in Nodejs]]></title><description><![CDATA[Every time I have to deal with emojis in Node.js, I have to google the same things over and over again. So I decided to write this post to have a reference for myself and you, dear reader.
If you know precisely why '😊'.length equals 2, you can skip ...]]></description><link>https://backend.cafe/how-to-handle-emojis-in-nodejs</link><guid isPermaLink="true">https://backend.cafe/how-to-handle-emojis-in-nodejs</guid><category><![CDATA[emoji]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Tue, 09 May 2023 06:50:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1682413421432/051cd88f-bba0-4366-9224-4764efbb16d3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every time I have to deal with emojis in Node.js, I have to google the same things over and over again. So I decided to write this post to have a reference for myself and you, dear reader.</p>
<p>If you know precisely why <code>'😊'.length</code> equals 2, you can skip this post; otherwise, let's see it together.</p>
<h2 id="heading-what-is-an-emoji">What is an emoji?</h2>
<p>An emoji is a pictogram, and they surround us in our daily life 🧐 But what is an emoji from a technical point of view?</p>
<p>It is a Unicode character, and <a target="_blank" href="https://home.unicode.org/technical-quick-start-guide/">Unicode</a> is a standard that defines a unique <strong>code point</strong> number for every character, letter, symbol, emoji, etc. in the world. A code point is a number that uniquely identifies a character, and it is represented in hexadecimal format: <code>U+&lt;hex code&gt;</code>.</p>
<p>Let's see some examples:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Character</td><td>Code points</td></tr>
</thead>
<tbody>
<tr>
<td>A</td><td><code>U+0041</code></td></tr>
<tr>
<td>😊</td><td><code>U+1F60A</code></td></tr>
<tr>
<td>🤌🏼</td><td><code>U+1F90C</code> <code>U+1F3FC</code></td></tr>
<tr>
<td>👨‍👧‍👦</td><td><code>U+1F468</code> <code>U+200D</code> <code>U+1F467</code> <code>U+200D</code> <code>U+1F466</code></td></tr>
</tbody>
</table>
</div><p>As you can see, the character <code>A</code> and the <code>😊</code> emoji are represented by a single code point, while <code>🤌🏼</code> and <code>👨‍👧‍👦</code> has multiple code points, contrary to what I said in the introduction 😱!</p>
<p>This is because some characters are special abstract symbols used to combine other characters to form a single one or change their properties.</p>
<p>In this case, we can have two types of characters:</p>
<ul>
<li><p><strong>Character modifiers</strong>: a special character modifies one near character. This is the case of the <code>🤌🏼</code> emoji, which is the combination of the <code>🤌</code>(<code>U+1F90C</code>) character and the <code>🏼</code>(<code>U+1F3FC</code>) character.</p>
</li>
<li><p><strong>Combining characters</strong>: a character that combines with other characters to form a single one or change its properties. This is the case of the <code>👨‍👧‍👦</code> emoji, which is the combination of:</p>
<ul>
<li><p><code>👨</code>(<code>U+1F468</code>)</p>
</li>
<li><p>plus the <code>ZWJ</code> (<code>U+200D</code>) + <code>👧</code>(<code>U+1F467</code>)</p>
</li>
<li><p>plus the <code>ZWJ</code> (<code>U+200D</code>) + <code>👦</code>(<code>U+1F466</code>)</p>
</li>
</ul>
</li>
</ul>
<p>Let's see them in detail.</p>
<h3 id="heading-character-modifiers">Character modifiers</h3>
<p>The Unicode standard defines a set of abstract characters that can be combined with other characters to change the appearance of another character or its phonetic transpilation.</p>
<p>An example is to change the emoji color by adding a <a target="_blank" href="https://en.wikipedia.org/wiki/Modifier_letter"><strong>modifier</strong></a> next to the emoji itself.<br />The colors are represented by five <a target="_blank" href="https://en.wikipedia.org/wiki/Fitzpatrick_scale">Fitzpatrick modifiers</a>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Modifier</td><td>Code point</td></tr>
</thead>
<tbody>
<tr>
<td>🏼</td><td><code>U+1F3FB</code></td></tr>
<tr>
<td>🏼</td><td><code>U+1F3FC</code></td></tr>
<tr>
<td>🏽</td><td><code>U+1F3FD</code></td></tr>
<tr>
<td>🏾</td><td><code>U+1F3FE</code></td></tr>
<tr>
<td>🏿</td><td><code>U+1F3FF</code></td></tr>
</tbody>
</table>
</div><p>So, if you print the <code>🤌</code> + <code>🏿</code>, every Unicode viewer will render the <code>🤌🏾</code> emoji.</p>
<h3 id="heading-combining-characters">Combining characters</h3>
<p>A combining character changes the character immediately before it by creating a new character.</p>
<p>This technique can be used to represent accents, diacritics, etc. An example is the <code>ñ</code> character, which is the combination of the <code>n</code>(<code>U+006E</code>) character and the <code>◌̃</code>(<code>U+0303</code>) character or the Japanese <code>ぴ</code> character which is <code>ひ</code>(<code>U+3072</code>) and <code>゚</code>(<code>U+309A</code>).</p>
<p>A special mention goes to the <code>ZWJ</code> (<code>U+200D</code>) <a target="_blank" href="https://en.wikipedia.org/wiki/Zero-width_joiner"><strong>zero-width joiner</strong></a> character, which is non-printable and is used to merge two or more characters into a single one. It is the case of the <code>👨‍👧‍👦</code> emoji, which is the combination of: <code>👨</code>(<code>U+1F468</code>) + <code>👧</code>(<code>U+1F467</code>) + <code>👦</code>(<code>U+1F466</code>) as we read earlier. Every char depends on the previous one.</p>
<p>Note that the <code>ZWJ</code> character is not the only one used to combine emojis, but it is the most common one for Arabic or Indic scripts such as <code>سلامU+200Dعلیکم</code> will be shown as <code>سلام‌علیکم</code> in a browser.</p>
<p>We did a long introduction about Unicode. Nevertheless, we just scratched the surface of the standard, which is very complex and huge. We did not mention the Unicode Planes or the Grapheme concept, but we have enough knowledge to understand why <code>'😊'.length</code> equals 2.</p>
<h2 id="heading-why-length-is-equal-to-2">Why <code>'😊'.length</code> is equal to 2</h2>
<p>Before answering this question, we must understand how strings are represented in Node.js. So far, we have talked about Unicode characters as <em>code points</em>, but Node.js does not understand what a <em>code point</em> is. It only understands <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Code_unit"><strong>code units</strong></a>.</p>
<p>A <strong>code unit</strong> is the smallest unit of information that a system can manipulate. For example, in a 7-bit ASCII system, a code unit is a single byte. In a 16-bit Unicode system, a code unit is a 16-bit word.</p>
<p>Node.js (and its underlying V8 engine) sees strings as a sequence of 16-bit Unicode characters, so one code unit is 16 bits long.</p>
<p>The final step is understanding how to convert a code point to a code unit.</p>
<p>Let's say we want to convert the <code>😊</code> (<code>U+1F60A</code>) emoji to its code units. To do it, we can write this low-level code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> codePoint = <span class="hljs-string">'U+1F60A'</span>; <span class="hljs-comment">// 😊</span>

<span class="hljs-comment">// Convert the code point to a string.</span>
<span class="hljs-comment">// Remember that a code point is a hexadecimal number.</span>
<span class="hljs-keyword">const</span> codePointAsInteger = codePoint.split(<span class="hljs-string">'U+'</span>).filter(<span class="hljs-function"><span class="hljs-params">c</span>=&gt;</span>c).map(<span class="hljs-function">(<span class="hljs-params">hex</span>) =&gt;</span> <span class="hljs-built_in">parseInt</span>(hex, <span class="hljs-number">16</span>));

<span class="hljs-comment">// Convert the code point to a string.</span>
<span class="hljs-keyword">const</span> codePointAsString = <span class="hljs-built_in">String</span>.fromCodePoint(...codePointAsInteger);

<span class="hljs-comment">// Convert the string input to an array of code units.</span>
<span class="hljs-keyword">const</span> codeUnits = toCodeUnits(codePointAsString);

<span class="hljs-built_in">console</span>.log({
  codePoint,
  codePointAsInteger,
  codePointAsString,
  <span class="hljs-attr">stringLength</span>: codePointAsString.length,
  codeUnits,
  <span class="hljs-attr">codeUnitAsString</span>: <span class="hljs-built_in">String</span>.fromCharCode(...codeUnits),
  <span class="hljs-attr">codeUnitsLength</span>: codeUnits.length,
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toCodeUnits</span>(<span class="hljs-params">string</span>) </span>{
  <span class="hljs-keyword">const</span> codeUnits = []

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; string.length; i++) {
    <span class="hljs-keyword">const</span> char = string[i]    
    <span class="hljs-keyword">const</span> codePoint = char.codePointAt(<span class="hljs-number">0</span>)
    codeUnits.push(codePoint)
  }

  <span class="hljs-keyword">return</span> codeUnits
}
</code></pre>
<p>In the code above we are executing the following steps:</p>
<ol>
<li><p>Convert the code point string to an integer array.</p>
</li>
<li><p>Convert the integer array to a string using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint"><code>String.fromCodePoint</code></a>.</p>
</li>
<li><p>Convert the string to an array of code units.</p>
</li>
<li><p>Print the results.</p>
</li>
</ol>
<blockquote>
<p>Note that the code is not optimized, and it is not intended to be used in production.</p>
</blockquote>
<p>The first step is to convert the code point string from the <code>U+&lt;hex&gt;</code> format to an integer array.</p>
<blockquote>
<p><strong>Note</strong><br />The code point can be declared as an integer just like this: <code>const codePoint = 0x1F60A;</code> <em>(hexadecimal representation)</em> or like a string <code>const codePoint = '\u{1F60A}';</code> in <em>UTF32 representation</em>, but I find more interesting to show how to convert it from a Unicode format.</p>
</blockquote>
<p>The second step is to convert the integer array to a string using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint"><code>String.fromCodePoint</code></a>. We did not use the <code>String.fromCharCode</code> method because it does not support Unicode code points greater than <code>0xFFFF</code> (or 65536 in decimal).</p>
<p>The third step is to convert the string to an array of code units. We did it by iterating over the string and calling the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt"><code>String.prototype.codePointAt</code></a> method on each character.</p>
<p>The last step should print the following output:</p>
<pre><code class="lang-js">{
  <span class="hljs-attr">codePoint</span>: <span class="hljs-string">'U+1F60A'</span>,
  <span class="hljs-attr">codePointAsInteger</span>: [ <span class="hljs-number">128522</span> ],
  <span class="hljs-attr">codePointAsString</span>: <span class="hljs-string">'😊'</span>,
  <span class="hljs-attr">stringLength</span>: <span class="hljs-number">2</span>,
  <span class="hljs-attr">codeUnits</span>: [ <span class="hljs-number">55357</span>, <span class="hljs-number">56842</span> ],
  <span class="hljs-attr">codeUnitAsString</span>: <span class="hljs-string">'😊'</span>,
  <span class="hljs-attr">codeUnitsLength</span>: <span class="hljs-number">2</span>
}
</code></pre>
<p>As we can see, the <code>😊</code> emoji is represented by two code units: <code>55357</code> and <code>56842</code>.</p>
<p>🏆 The Node.js string length is equal to the number of its code units. So, if the string has characters that require more than one code unit to be displayed, the string length will be greater than the character length.</p>
<h2 id="heading-technical-explanation">Technical explanation</h2>
<p>Previously we said that Node.js stores strings as a sequence of 16-bit Unicode characters. This means that it can store up to 65536 different characters. However, Unicode defines more than 65536 characters, so it uses a technique called <a target="_blank" href="https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF"><strong>surrogate pairs</strong></a>. Every character that requires more than 16 bits to be represented is split into two 16-bit code units.</p>
<p>Now, try to check the output for these code points:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> codePoint = <span class="hljs-string">'U+1F468U+200DU+1F467U+200DU+1F466'</span>; <span class="hljs-comment">// 👨‍👧‍👦</span>
<span class="hljs-keyword">const</span> codePoint = <span class="hljs-string">'U+1F90CU+1F3FC'</span> <span class="hljs-comment">// 🤌🏼</span>
</code></pre>
<p>You will be surprised that the string length is 8 and 4, respectively.</p>
<h3 id="heading-how-to-count-the-number-of-characters-in-a-string">How to count the number of characters in a string</h3>
<p>But how can we count the number of characters in a string without using the <code>String.length</code> property?</p>
<p>A common solution is to use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize"><code>String.normalize</code></a> method to convert the string to a <a target="_blank" href="https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization"><strong>normalization form</strong></a>. Then, using the spread operator to convert the string to an array of <strong>code points</strong> will give us the number of characters in the string.</p>
<pre><code class="lang-js">[...<span class="hljs-string">'ñañaña'</span>.normalize()].length; <span class="hljs-comment">// 6</span>
[...<span class="hljs-string">'ñañaña'</span>].length; <span class="hljs-comment">// 9</span>
</code></pre>
<p>In this example, the <code>ñ</code> is represented by two code points <code>n</code>(<code>U+006E</code>) + <code>◌̃</code>(<code>U+0303</code>), but the <code>normalize</code> method will convert it to a single code point <code>ñ</code>(<code>U+00F1</code>).</p>
<p>Unfortunately, this does not work for all emojis:</p>
<pre><code class="lang-plaintext">[...'😊'.normalize()].length // 1
[...'🤌🏼'.normalize()].length // It is still 2!!!
</code></pre>
<p>So, we must adopt a new strategy by using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter"><code>Intl.Segmenter</code></a> to split the string into segments and count the number of segments.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> segmenter = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.Segmenter(<span class="hljs-string">'en'</span>, { <span class="hljs-attr">granularity</span>: <span class="hljs-string">'grapheme'</span> });
[...segmenter.segment(<span class="hljs-string">'🤌🏼'</span>)].length <span class="hljs-comment">// 1 🎉</span>
</code></pre>
<blockquote>
<p><strong>Note</strong><br />It is available in Node.js since version 16.0.0 and it is not supported by all the browsers.</p>
</blockquote>
<p>You may choose the best solution for your use case.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we learned how to convert a Unicode code point to a code unit and why <code>String.length</code> is not equal to the number of characters in a string. I tried to explain the concepts in a simple and accessible way. If you want to learn more about Unicode and Node.js, I recommend you to read these detailed articles:</p>
<ul>
<li><p>https://mathiasbynens.be/notes/javascript-unicode</p>
</li>
<li><p>https://dmitripavlutin.com/what-every-javascript-developer-should-know-about-unicode/</p>
</li>
<li><p><a target="_blank" href="https://tonsky.me/blog/unicode/">https://tonsky.me/blog/unicode/</a></p>
</li>
</ul>
<p>Other useful resources that I used to debug things are:</p>
<ul>
<li><p>https://github.com/node-unicode/node-unicode-data to get all the Unicode data in Node.js.</p>
</li>
<li><p>https://codepoints.net/ to explore Unicode characters and code points.</p>
</li>
<li><p>https://www.compart.com/en/unicode/ to explore Unicode planes and groups.</p>
</li>
</ul>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item><item><title><![CDATA[The Secrets Behind High Performance With Node.js]]></title><description><![CDATA[I always strive to write actionable articles, but let’s face it: improving code performance can be a bit intimidating.
Not today, my friends!In this post, I’ll share some lesser-known but highly effective strategies to speed up your codebase — starti...]]></description><link>https://backend.cafe/the-secrets-behind-high-performance-with-nodejs</link><guid isPermaLink="true">https://backend.cafe/the-secrets-behind-high-performance-with-nodejs</guid><category><![CDATA[performance]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Regex]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sun, 16 Apr 2023 07:10:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/9zH3AfW7O4o/upload/8ce6ddf621c3b5f471bea72f161c950e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I always strive to write actionable articles, but let’s face it: improving code performance can be a bit intimidating.
Not today, my friends!<br />In this post, I’ll share some lesser-known but highly effective strategies to speed up your codebase — starting tomorrow.<br />Get ready to learn some serious performance hacks!</p>
<p>During a starry evening, I happened to remember what I thought was a performant solution compared to what I see in the Node.js panorama,
and I think it's worth sharing.</p>
<p>What I thought is that the fastest code implementation is achieved by using:</p>
<ul>
<li>Esoteric data structures</li>
<li>Unknown algorithms</li>
<li>Bitwise operations</li>
<li>A master degree in rocket science</li>
</ul>
<p>...and I was wrong!</p>
<p>Let me share some of the secrets behind high performance with Node.js.</p>
<h2 id="heading-regular-expressions-are-slow">Regular expressions are slow</h2>
<p>The title explains it all — regular expressions are slow. If you see a regular expression in your code then that code can be optimized by removing the regular expression.</p>
<p>The error I was making is to think that regular expressions have a complexity of <code>O(1)</code>, but they don't.<br /><em>I know, I know, I was silly to think that.</em><br />Anyway, the regular expression complexity depends on the engine implementation.
In Node.js, the <code>RegExp</code> engine is performed by V8 v10.2.x, which implements a
backtracking algorithm with a complexity of <code>O(2<sup>n</sup>)</code>!</p>
<p>In fact, if you try to run this simple code:</p>
<pre><code class="lang-js">/(a*)*b/.exec(<span class="hljs-string">'a'</span>.repeat(<span class="hljs-number">100</span>))
</code></pre>
<p>It will take 90 seconds.<br />Instead, if you run the previous code with <code>node --enable-experimental-regexp-engine-on-excessive-backtracks regexp.js</code>,
it will take only <strong>0,01 seconds</strong>!!</p>
<p>The V8 flag <code>--enable-experimental-regexp-engine-on-excessive-backtracks</code> enables a new experimental non-backtracking RegExp engine.
You can deepen this new algorithm by following the guidance in this <a target="_blank" href="https://v8.dev/blog/non-backtracking-regexp">V8 article</a>.</p>
<p>So, the first takeaway is that regular expressions are slow, and you should avoid them as much as possible.
If you can't avoid them, you should try to check the <code>node --v8-options</code> and verify if you can tune the default RegExp engine.</p>
<p>If you are interested in the topic then you can find out more by reading <a target="_blank" href="https://swtch.com/~rsc/regexp/regexp1.html">this article</a>. It explains the complexity of regular expressions and compares the different algorithms.</p>
<h2 id="heading-you-dont-need-regular-expressions">You don't need regular expressions</h2>
<p>Sorry to be so repetitive, but I must talk about regular expressions again.<br />Sometimes a regular expression seems to be the best solution, but it's not.</p>
<p>Let's take a look at the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> utf8MimeType = <span class="hljs-regexp">/^text\/|^application\/(javascript|json)/</span>
<span class="hljs-keyword">const</span> isUtf8MimeType = utf8MimeTypeRE.test(<span class="hljs-string">'application/json'</span>)
</code></pre>
<p>The code above is a simple check to verify if a MIME type is UTF-8 and matches multiple combinations of MIME types.</p>
<p>The code is simple, but it's not performant because it can be optimized by removing the regular expression:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isUtf8MimeType</span> (<span class="hljs-params">value</span>) </span>{
  <span class="hljs-keyword">const</span> len = value.length
  <span class="hljs-keyword">return</span> (
    (len &gt; <span class="hljs-number">21</span> &amp;&amp; value.indexOf(<span class="hljs-string">'application/javascript'</span>) === <span class="hljs-number">0</span>) ||
    (len &gt; <span class="hljs-number">14</span> &amp;&amp; value.indexOf(<span class="hljs-string">'application/json'</span>) === <span class="hljs-number">0</span>) ||
    (len &gt; <span class="hljs-number">5</span> &amp;&amp; value.indexOf(<span class="hljs-string">'text/'</span>) === <span class="hljs-number">0</span>)
  )
}

<span class="hljs-keyword">const</span> isUtf8MimeType = isUtf8MimeType(<span class="hljs-string">'application/json'</span>)
</code></pre>
<p>The latter code is faster than the former code because it doesn't use a regular expression.<br />What does "faster" mean?<br />The first code executes 10,632,205 ops/sec ±1.69% (191 runs sampled), while
the second code executes 1,132,847,245 ops/sec ±0.15% (192 runs sampled).
The second code is 106x times faster than the first code.</p>
<p>The code example is not trivial because it is one of <a target="_blank" href="https://github.com/fastify/send/pull/39">Fastify's pieces of code</a>! <a target="_blank" href="https://github.com/Uzlopak">Uzlopak</a> is one of the Fastify team members obsessed with performance.</p>
<h2 id="heading-silly-code-is-not-so-silly">Silly code is not so silly</h2>
<p>There may be times when you think the source code is silly, but it's not.<br />Let's normalize an unknown input into a string array. If we can't normalize the input, we throw an error:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">normalizeList</span> (<span class="hljs-params">val</span>) </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> val === <span class="hljs-string">'string'</span>) {
    <span class="hljs-keyword">return</span> [val]
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (val === <span class="hljs-literal">false</span>) {
    <span class="hljs-keyword">return</span> []
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(val)) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>, il = val.length; i &lt; il; ++i) {
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> val[i] !== <span class="hljs-string">'string'</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'must be array of strings or false'</span>)
      }
    }
    <span class="hljs-keyword">return</span> val
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'must be array of strings or false'</span>)
  }
}
</code></pre>
<p>I'm sure this code works, but it may not pass a coding interview — it's too verbose, and there is a smell of duplication... but <a target="_blank" href="https://github.com/fastify/send/pull/38">it is performant</a>!
The code above is 4x times faster than the nice and clean code below:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">normalizeList</span> (<span class="hljs-params">val</span>) </span>{
  <span class="hljs-keyword">const</span> list = [].concat(val || [])
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; list.length; i++) {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> list[i] !== <span class="hljs-string">'string'</span>) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'must be array of strings or false'</span>)
    }
  }
  <span class="hljs-keyword">return</span> list
}
</code></pre>
<p>So it is important to understand that sometimes simple checks are faster than a single line of code.
Let's call it an <code>exit early</code> pattern.<br />This doesn't mean that you should write verbose code, but you should be aware how to improve the performance:</p>
<ul>
<li>Conditions that are more likely to be true should be checked first</li>
<li>Simple checks are faster than forcing a unique code path</li>
<li>A bit of duplication is better than a bit less performance</li>
</ul>
<p>Another interesting example is to map all the supported ASCII characters to a <code>Map</code> object.
In Fastify <a target="_blank" href="https://github.com/fastify/fast-uri/pull/7/files">we did this too</a>, and all in the name of performance.
This new code helps us to reduce the complexity of the code by treating uppercase and lowercase characters as the same.
For sure, this code would not pass a coding interview as well!</p>
<h3 id="heading-performance-shortcuts">Performance shortcuts</h3>
<p>Earlier, I mentioned that you should write readable code and prefer simple checks. However, it's worth mentioning that sometimes you may isolate a piece of code that is not readable, but it is faster.</p>
<p>A perfect example is the <code>new Function</code> usage. By writing:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> functionArgs = [<span class="hljs-string">'value'</span>]
<span class="hljs-keyword">const</span> functionBody = <span class="hljs-string">'return value[0].toUpperCase()'</span>
<span class="hljs-keyword">const</span> fn = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(...functionArgs, functionBody)

<span class="hljs-built_in">console</span>.log(fn(<span class="hljs-string">'Hello'</span>)) <span class="hljs-comment">// prints H</span>
</code></pre>
<p>You can create a function body at runtime by concatenating strings.</p>
<p>This technique is the secret sauce of Fastify's <a target="_blank" href="https://github.com/fastify/fast-json-stringify"><code>fast-json-stringify</code></a> module.
Another extreme example is the <code>string.startsWith</code> and <code>string.endsWith</code> methods.<br />If we want to check that a string starts with the <code>foo</code> string, we can generate a function body like the following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> functionArgs = [<span class="hljs-string">'value'</span>]
<span class="hljs-keyword">const</span> functionBody = <span class="hljs-string">`return value[0] === 'f' &amp;&amp; value[1] === 'o' &amp;&amp; value[2] === 'o'`</span>
<span class="hljs-keyword">const</span> fn = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(...functionArgs, functionBody)

<span class="hljs-built_in">console</span>.log(fn(<span class="hljs-string">'foo'</span>)) <span class="hljs-comment">// prints true</span>
<span class="hljs-built_in">console</span>.log(fn(<span class="hljs-string">'bar'</span>)) <span class="hljs-comment">// prints false</span>
</code></pre>
<p>The generated function is <strong>14x</strong> times faster than the native method!
This is the technique <a target="_blank" href="https://github.com/fastify/help/issues/711">likely to be integrated</a> into Fastify's code.</p>
<p>⚠️ Note that generating a function body at runtime could be unsafe if you are managing user input to generate the function.
Be careful when you use this technique to make sure it improves the performance!</p>
<h2 id="heading-know-your-language-and-runtime">Know your language and runtime</h2>
<p>Node.js is a JavaScript runtime, but it has its own peculiarities that you should know in order to be able to write performant code.
If you deal with the <code>Error</code> object, or you have ever implemented a custom error, you should know about the <a target="_blank" href="https://nodejs.org/api/errors.html#errorstacktracelimit"><code>Error.stackTraceLimit</code></a></p>
<p>The <code>Error.stackTraceLimit</code> is the maximum number of stack frames captured by the <code>Error.stack</code> property.
The default value is <code>10</code>, but you can change it to <code>0</code> when you don't need the complete stack trace, and this will improve the performance by 600x times!</p>
<p>This is an example of the most uncommon property of Node.js, but it is not always about tweaking the runtime,
sometimes you need to know the best constructs to use in your code.
<a target="_blank" href="https://github.com/RafaelGSS">RafaelGSS</a> is a Node.js core contributor, and he has
written a <a target="_blank" href="https://github.com/RafaelGSS/nodejs-bench-operations">benchmark</a> to compare the performance of the different operations in JavaScript.<br />For example:</p>
<ul>
<li>Concatenating strings using <code>['a', 'b', 'c'].join('')</code> is 100x slower than <code>a + b + c</code>!</li>
<li>The <code>foreach</code> loop is ~5% slower than the <code>for</code> loop!</li>
</ul>
<p>The takeaway is to write readable and maintainable code, then optimize it by isolating the hot path and benchmarking it. The last step is to optimize the hot path, focusing on tiny pieces of code.</p>
<h2 id="heading-use-the-right-data-structure">Use the right data structure</h2>
<p>The data structure you use in your code can greatly impact your application's performance.</p>
<blockquote>
<p>There is no slow programming language, just bad algorithms and data structures design.</p>
</blockquote>
<p>Says <a target="_blank" href="https://github.com/micheleriva">Michele Riva</a> in his talk at <a target="_blank" href="https://www.youtube.com/watch?v=42sMkbGLlh4">NodeConf EU 2022</a>.
That is incredibly true, and you should always consider the data structure you use in your code.</p>
<p>If you are looping an array repeatedly, it is essential to ask yourself if you can use a <code>Map</code> or a <code>Set</code> instead.
A tree data structure is sometimes the best solution to run performant searches and traversals.</p>
<p>I can't suggest a simple rule for you to follow, but you should always think about the data structure you are using
and the algorithm that uses it.<br />I find it useful to check the <a target="_blank" href="https://www.bigocheatsheet.com/">Big O notation cheat sheet</a> of the data structure
and to read some books about these topics.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we have seen how to improve the performance of a Node.js application by using and writing
performant code and avoiding unnecessary operations.</p>
<p>Now, think about the application you are working on and try to speed it up by using the techniques we have highlighted in this article.</p>
<p>If you enjoyed this article, comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!
Remember to follow <a target="_blank" href="https://github.com/Uzlopak">Uzlopak</a> on GitHub to see his amazing work on Fastify!</p>
]]></content:encoded></item><item><title><![CDATA[Dynamic GraphQL queries with Mercurius]]></title><description><![CDATA[If you're using Fastify with Mercurius as your GraphQL adapter, you may be looking for some advanced usages.
In this article, we'll explore a real world example with Dynamic GQL queries with Mercurius.

The example we will discuss is only one scenari...]]></description><link>https://backend.cafe/dynamic-graphql-queries-with-mercurius</link><guid isPermaLink="true">https://backend.cafe/dynamic-graphql-queries-with-mercurius</guid><category><![CDATA[backend]]></category><category><![CDATA[mercurius]]></category><category><![CDATA[fastify]]></category><category><![CDATA[GraphQL]]></category><dc:creator><![CDATA[Manuel Spigolon]]></dc:creator><pubDate>Sun, 19 Mar 2023 07:45:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/A48Mgmjd10Y/upload/b20350cab10580091a7c89e44de3b887.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're using <a target="_blank" href="https://www.fastify.io">Fastify</a> with <a target="_blank" href="https://github.com/mercurius-js/mercurius">Mercurius</a> as your GraphQL adapter, you may be looking for some advanced usages.
In this article, we'll explore a real world example with Dynamic GQL queries with Mercurius.</p>
<blockquote>
<p>The example we will discuss is only one scenario that you may encounter as Software Engineer at NearForm!
If you want to solve these kind of problems, <a target="_blank" href="https://grnh.se/18177b983us">we are hiring!</a></p>
</blockquote>
<h2 id="heading-what-are-dynamic-graphql-queries">What are Dynamic GraphQL queries?</h2>
<p>A dynamic GraphQL query is a query that is constructed at runtime, based on the needs of the client.
This is useful when the client is uncertain of the structure of the data and needs to retrieve specific data based on conditions,
or when the client needs to retrieve a subset of data depending on the user's role or permissions.</p>
<p>In our use case, we want to invert the control of query dynamicity from the client to the server.
This means that the client will send a standard GQL query and the server will return the data based on:</p>
<ul>
<li>The client's query</li>
<li>The user's role</li>
</ul>
<p>It's important to note that we are not talking about returning a subset of a generic GraphQL type,
but a <strong>completely different GraphQL type</strong>.</p>
<h2 id="heading-defining-the-schema">Defining the schema</h2>
<p>When creating a GraphQL server, the first step is to define the schema.
The GraphQL specification provides clear guidance on how to accomplish our target by utilizing <a target="_blank" href="https://graphql.org/learn/schema/#union-types">GraphQL Unions</a>.</p>
<p>Unions in GraphQL allow for multiple types to be returned from a single field, making it a powerful tool for querying related data.
This can be especially useful when retrieving data from multiple types in a single query.</p>
<p>To begin, let's define our schema using GraphQL Unions.</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> Query {
  <span class="hljs-symbol">searchData:</span> Grid
}

<span class="hljs-keyword">union</span> Grid = AdminGrid | ModeratorGrid | UserGrid


<span class="hljs-keyword">type</span> AdminGrid {
  <span class="hljs-symbol">totalRevenue:</span> Float
}

<span class="hljs-keyword">type</span> ModeratorGrid {
  <span class="hljs-symbol">banHammer:</span> Boolean
}

<span class="hljs-keyword">type</span> UserGrid {
  <span class="hljs-symbol">basicColumn:</span> String
}
</code></pre>
<p>As you can see, our schema includes a Query type with a <code>searchData</code> field that returns a <code>Grid</code> union type.
This Grid union type can represent one of three possible types: <code>AdminGrid</code>, <code>ModeratorGrid</code>, or <code>UserGrid</code>.</p>
<p>Currently, the client can send a query using <a target="_blank" href="https://graphql.org/learn/queries/#inline-fragments">inline fragments</a> like this:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> {
  searchData {
    <span class="hljs-punctuation">... </span><span class="hljs-keyword">on</span> AdminGrid {
      totalRevenue
    }
    <span class="hljs-punctuation">... </span><span class="hljs-keyword">on</span> ModeratorGrid {
      banHammer
    }
    <span class="hljs-punctuation">... </span><span class="hljs-keyword">on</span> UserGrid {
      basicColumn
    }
  }
}
</code></pre>
<p>While this is a valid query, <strong>it is not the desired outcome</strong>.
We aim to return a different type based on the user's role, so that the client can send a query like this:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> {
  searchData {
    totalRevenue
  }
}
</code></pre>
<p>It's important to note that the above query is not valid against the schema defined above,
but with some additional implementation, <strong>we can make it work</strong>!</p>
<h2 id="heading-implementing-the-business-logic">Implementing the business logic</h2>
<p>Let's start building the basic implementation of our server.
You can skip this section if you are already familiar with Fastify and Mercurius.</p>
<p>The first step is to install the necessary dependencies.</p>
<pre><code class="lang-bash">mkdir graphql-dynamic-queries
<span class="hljs-built_in">cd</span> graphql-dynamic-queries
npm init -y
npm install fastify@4 mercurius@11
</code></pre>
<p>Now, copy the schema we defined above in a <code>gql-schema.js</code> file,
then you need to create an <code>app.js</code> file where we will write our server:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify'</span>)
<span class="hljs-keyword">const</span> GQL = <span class="hljs-built_in">require</span>(<span class="hljs-string">'mercurius'</span>)
<span class="hljs-keyword">const</span> schema = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./gql-schema'</span>)

<span class="hljs-comment">// For simplicity, we will start the server only if started with `node app.js run`</span>
<span class="hljs-keyword">if</span> (process.argv[<span class="hljs-number">2</span>] === <span class="hljs-string">'run'</span>) {
  buildApp()
    .then(<span class="hljs-function"><span class="hljs-params">app</span> =&gt;</span> app.listen({ <span class="hljs-attr">port</span>: <span class="hljs-number">8080</span> }))
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buildApp</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = Fastify({ <span class="hljs-attr">logger</span>: <span class="hljs-literal">true</span> })

  <span class="hljs-keyword">await</span> app.register(GQL, {
    schema,
    <span class="hljs-attr">resolvers</span>: {
      <span class="hljs-attr">Query</span>: {
        <span class="hljs-attr">searchData</span>: <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">root, args, context, info</span>) </span>{
          <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> implement the business logic</span>
          <span class="hljs-keyword">return</span> {}
        }
      },
      <span class="hljs-attr">Grid</span>: {
        resolveType (obj, context) {
          <span class="hljs-keyword">return</span> getUserType(context)
        }
      }
    }
  })

  <span class="hljs-keyword">return</span> app
}

<span class="hljs-built_in">module</span>.exports = buildApp

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserType</span> (<span class="hljs-params">context</span>) </span>{
  <span class="hljs-keyword">switch</span> (context.reply.request.headers[<span class="hljs-string">'x-user-type'</span>]) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'admin'</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'AdminGrid'</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">'moderator'</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'ModeratorGrid'</span>
    <span class="hljs-attr">default</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'UserGrid'</span>
  }
}
</code></pre>
<p>To verify that everything is working, you can start the server running <code>node app.js run</code> and you should
see the following output:</p>
<pre><code class="lang-bash">Server listening at http://127.0.0.1:8080
</code></pre>
<p>Great! Now we must list all the use cases we want to implement by adding some test cases.</p>
<h3 id="heading-testing-our-server">Testing our server</h3>
<p>We want to test our server with the following use cases:</p>
<ul>
<li>A user with the <code>admin</code> role should be able to retrieve the <code>totalRevenue</code> field without inline fragments</li>
<li>A user with the <code>moderator</code> role should be able to retrieve the <code>banHammer</code> field without inline fragments</li>
<li>A user with the <code>user</code> role should be able to retrieve the <code>basicColumn</code> field without inline fragments</li>
<li>A user without the <code>admin</code> role should not be able to retrieve the <code>totalRevenue</code> field</li>
<li>A user without the <code>moderator</code> role should not be able to retrieve the <code>banHammer</code> field</li>
<li>A user without the <code>user</code> role should not be able to retrieve the <code>basicColumn</code> field</li>
<li>A user without any role should not be able to retrieve any field</li>
</ul>
<p>We must install the required dependencies:</p>
<pre><code class="lang-bash">npm install tap@15 -D
</code></pre>
<p>We can write these tests in a <code>test.js</code> file. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { test } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'tap'</span>)

<span class="hljs-keyword">const</span> buildApp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./app'</span>)

test(<span class="hljs-string">'A user with the `admin` role should be able to retrieve the `totalRevenue` field without inline fragments'</span>, <span class="hljs-keyword">async</span> t =&gt; {
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> buildApp()
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> doQuery(app, <span class="hljs-string">'admin'</span>, <span class="hljs-string">`
    query {
      searchData {
        totalRevenue
      }
    }
  `</span>)

  t.same(res.data.searchData, { <span class="hljs-attr">totalRevenue</span>: <span class="hljs-number">42</span> })
})

<span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> add the other tests</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doQuery</span> (<span class="hljs-params">app, userType, query</span>) </span>{
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> app.inject({
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">url</span>: <span class="hljs-string">'/graphql'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'x-user-type'</span>: userType
    },
    <span class="hljs-attr">body</span>: {
      query
    }
  })

  <span class="hljs-keyword">return</span> res.json()
}
</code></pre>
<p>For the sake of simplicity, we will not list all the tests here,
but you can find the complete complete source code in the <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-dynamic-queries">GitHub repository</a>.</p>
<p>Running the tests with <code>node test.js</code> will fail because we have not implemented the business logic yet.
So, let's start writing the code!</p>
<h2 id="heading-implementing-the-server-side-dynamic-queries">Implementing the server-side Dynamic Queries</h2>
<p>To implement the business logic, there are these main steps:</p>
<ol>
<li>Retrieve the user's role from the request headers</li>
<li>Manage the GraphQL query to return the correct type based on the user's role</li>
</ol>
<p>Let's solve the first point.</p>
<h3 id="heading-how-to-retrieve-the-users-role">How to retrieve the user's role</h3>
<p>We can implement the user role retrieval by installing the <a target="_blank" href="https://github.com/mercurius-js/auth"><code>mercurius-auth</code></a> plugin.</p>
<pre><code class="lang-bash">npm i mercurius-auth@3
</code></pre>
<p>Then, we can register the plugin in our <code>app.js</code> file.
To understand what the plugin does, you can read its documentation.</p>
<p>In the following example, we will compare the <code>x-user-type</code> HTTP header with the <code>@auth</code> directive we are going to define in the schema.
If they match, the user will be authorized to access the field and run the query.</p>
<p>Let's start by defining the <code>@auth</code> directive in the schema:</p>
<pre><code class="lang-graphql">directive <span class="hljs-meta">@auth</span>(
  <span class="hljs-symbol">role:</span> String
) <span class="hljs-keyword">on</span> OBJECT

<span class="hljs-comment"># ..same as before</span>

<span class="hljs-keyword">type</span> AdminGrid <span class="hljs-meta">@auth</span>(<span class="hljs-symbol">role:</span> <span class="hljs-string">"admin"</span>) {
  <span class="hljs-symbol">totalRevenue:</span> Float
}

<span class="hljs-keyword">type</span> ModeratorGrid <span class="hljs-meta">@auth</span>(<span class="hljs-symbol">role:</span> <span class="hljs-string">"moderator"</span>) {
  <span class="hljs-symbol">banHammer:</span> Boolean
}

<span class="hljs-keyword">type</span> UserGrid <span class="hljs-meta">@auth</span>(<span class="hljs-symbol">role:</span> <span class="hljs-string">"user"</span>) {
  <span class="hljs-symbol">basicColumn:</span> String
}
</code></pre>
<p>Then, we can register the plugin in our <code>app.js</code> file and implement a simple <code>searchData</code> resolver:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buildApp</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = Fastify() <span class="hljs-comment">// the same as before</span>

  <span class="hljs-keyword">await</span> app.register(GQL, {
    schema,
    <span class="hljs-attr">resolvers</span>: {
      <span class="hljs-attr">Query</span>: {
        <span class="hljs-attr">searchData</span>: <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">root, args, context, info</span>) </span>{
          <span class="hljs-keyword">switch</span> (getUserType(context)) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">'AdminGrid'</span>:
              <span class="hljs-keyword">return</span> { <span class="hljs-attr">totalRevenue</span>: <span class="hljs-number">42</span> }

            <span class="hljs-keyword">case</span> <span class="hljs-string">'ModeratorGrid'</span>:
              <span class="hljs-keyword">return</span> { <span class="hljs-attr">banHammer</span>: <span class="hljs-literal">true</span> }

            <span class="hljs-attr">default</span>:
              <span class="hljs-keyword">return</span> { <span class="hljs-attr">basicColumn</span>: <span class="hljs-string">'basic'</span> }
          }
        }
      },
    },
    <span class="hljs-attr">Grid</span>: {} <span class="hljs-comment">// the same as before </span>
  })

  app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'mercurius-auth'</span>), {
    authContext (context) {
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">identity</span>: context.reply.request.headers[<span class="hljs-string">'x-user-type'</span>]
      }
    },
    <span class="hljs-keyword">async</span> applyPolicy (policy, parent, args, context, info) {
      <span class="hljs-keyword">const</span> role = policy.arguments[<span class="hljs-number">0</span>].value.value
      app.log.info(<span class="hljs-string">'Applying policy %s on user %s'</span>, role, context.auth.identity)

      <span class="hljs-comment">// we compare the schema role directive with the user role</span>
      <span class="hljs-keyword">return</span> context.auth.identity === role
    },
    <span class="hljs-attr">authDirective</span>: <span class="hljs-string">'auth'</span>
  })

  <span class="hljs-keyword">return</span> app
}
</code></pre>
<p>Now, the user should be able to retrieve the <code>totalRevenue</code> field only if the <code>x-user-type</code> header is set to <code>admin</code>.</p>
<p>Nevertheless, we can't run the tests yet because we have not implemented the second point.</p>
<h3 id="heading-implementing-the-dynamic-queries">Implementing the Dynamic Queries</h3>
<p>The last step is to implement the dynamic queries.
Right now, our tests are failing with the following error:</p>
<blockquote>
<p>"Cannot query field 'totalRevenue' on type 'Grid'. Did you mean to use an inline fragment on 'AdminGrid'?</p>
</blockquote>
<p>The error is correct because we are not using inline fragments to query a Union type.</p>
<p>To overcome this issue, we can use the mercurius <a target="_blank" href="https://github.com/mercurius-js/mercurius/blob/master/docs/hooks.md#prevalidation"><code>preValidation</code></a> hook.
Let's try to see how it works:</p>
<p><a target="_blank" href="https://mermaid.live/edit#pako:eNptkMFuwjAMhl_F8qlI8AI9TIIG7bIdNnYjHKLE0AqSFCeZQJR3x213nE-W_8_-bT_QRkdY44lN38KP0gEk1vvm0lHIB1it3ob3rw-4FuL7AJv9jviX-DBzm1GHpuo5WkoJRlJFW7z0LmakmRBV2ZbsGUzJLTiTzZ-qJnVb0Y1syfTfgO2EDN-U-hgSDWtcoif2pnOy92OENOaWPGmsJXWGzxp1eAondnF3DxbrzIWWWHrxJtUZOddjfTSXJFVyXY78OT9i-sfzBTsWWD4"><img src="https://mermaid.ink/img/pako:eNptkMFuwjAMhl_F8qlI8AI9TIIG7bIdNnYjHKLE0AqSFCeZQJR3x213nE-W_8_-bT_QRkdY44lN38KP0gEk1vvm0lHIB1it3ob3rw-4FuL7AJv9jviX-DBzm1GHpuo5WkoJRlJFW7z0LmakmRBV2ZbsGUzJLTiTzZ-qJnVb0Y1syfTfgO2EDN-U-hgSDWtcoif2pnOy92OENOaWPGmsJXWGzxp1eAondnF3DxbrzIWWWHrxJtUZOddjfTSXJFVyXY78OT9i-sfzBTsWWD4?type=png" alt /></a></p>
<p>When a client sends a GraphQL query, the server will process the GQL Document in the <code>preValidation</code> hook,
before validating the GQL against the GQL Schema.</p>
<p>In this hook, <strong>we can modify the GQL Document</strong> sent by the user to add the inline fragments.
So, after the <code>mercurius-auth</code> plugin registration, we can add the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buildApp</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = Fastify() <span class="hljs-comment">// the same as before</span>

  <span class="hljs-keyword">await</span> app.register(GQL, {
    <span class="hljs-comment">// the same as before </span>
  })

  app.register(<span class="hljs-built_in">require</span>(<span class="hljs-string">'mercurius-auth'</span>), {
    <span class="hljs-comment">// the same as before</span>
  })

  app.graphql.addHook(<span class="hljs-string">'preValidation'</span>, <span class="hljs-keyword">async</span> (schema, <span class="hljs-built_in">document</span>, context) =&gt; {
    <span class="hljs-keyword">const</span> { visit } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'graphql'</span>)

    <span class="hljs-keyword">const</span> userType = getUserType(context)

    <span class="hljs-keyword">const</span> newDocument = visit(<span class="hljs-built_in">document</span>, {
      <span class="hljs-attr">enter</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
        <span class="hljs-comment">// We check if we must add the inline fragment</span>
        <span class="hljs-keyword">const</span> isMaskedQuery = node.kind === <span class="hljs-string">'Field'</span> &amp;&amp;
                              node.name.value === <span class="hljs-string">'searchData'</span> &amp;&amp;
                              node.selectionSet.selections.length === <span class="hljs-number">1</span> &amp;&amp;
                              node.selectionSet?.selections.length &gt; <span class="hljs-number">0</span>

        <span class="hljs-keyword">if</span> (isMaskedQuery) {
          <span class="hljs-comment">// This is the magic, we add a new inline fragment modifying the AST</span>
          <span class="hljs-keyword">const</span> nodeWithInlineFragment = {
            ...node,
            <span class="hljs-attr">selectionSet</span>: {
              <span class="hljs-attr">kind</span>: <span class="hljs-string">'SelectionSet'</span>,
              <span class="hljs-attr">selections</span>: [
                {
                  <span class="hljs-attr">kind</span>: <span class="hljs-string">'InlineFragment'</span>,
                  <span class="hljs-attr">typeCondition</span>: {
                    <span class="hljs-attr">kind</span>: <span class="hljs-string">'NamedType'</span>,
                    <span class="hljs-attr">name</span>: {
                      <span class="hljs-attr">kind</span>: <span class="hljs-string">'Name'</span>,
                      <span class="hljs-attr">value</span>: userType
                    }
                  },
                  <span class="hljs-attr">selectionSet</span>: node.selectionSet
                }
              ]
            }
          }
          <span class="hljs-keyword">return</span> nodeWithInlineFragment
        }
      }
    })

    <span class="hljs-comment">// We apply the new AST to the GQL Document</span>
    <span class="hljs-built_in">document</span>.definitions = newDocument.definitions
  })

  <span class="hljs-keyword">return</span> app
}
</code></pre>
<p>In the previous code, we inspect the GraphQL <a target="_blank" href="(https://backend.cafe/what-is-ast)">Document Abstract Syntax Tree (AST)</a> to determine if we need to add the inline fragment programmatically.
If we recognize the query as a masked query, we add the inline fragment to the AST and return the modified AST to the GraphQL Document.</p>
<p>By doing this, Mercurius will process the modified GraphQL Document as if the user had sent it with the inline fragment. This means that it will:</p>
<ul>
<li>Validate the GraphQL Document against the GraphQL Schema</li>
<li>Apply the authentication policy</li>
<li>Resolve the query</li>
</ul>
<p>With this implementation, when running tests, everything should pass successfully.</p>
<h2 id="heading-summary">Summary</h2>
<p>We have seen how versatile Mercurius is and how simple it is to implement features like dynamic queries in a complex scenario.
While the goal may seem challenging, this server-side implementation provides several benefits such as:</p>
<ul>
<li>Avoiding breaking changes on the client side to support new features</li>
<li>Hiding GraphQL Types from the client through disabling schema introspection</li>
<li>Applying query optimizations for the client</li>
<li>Providing a specialized response based on the user type instead of a generic one.</li>
</ul>
<p>This is just a small example of the possibilities when using Mercurius and manipulating the GraphQL Document.</p>
<p>If you have found this helpful, you may read <a target="_blank" href="https://backend.cafe/series/mercurius">other articles about Mercurius</a>.</p>
<p>Now jump into the <a target="_blank" href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-dynamic-queries">source code on GitHub</a> and start to play with the GraphQL implemented in Fastify.</p>
<p>If you enjoyed this article comment, share and follow me on <a target="_blank" href="https://twitter.com/ManuEomm">Twitter</a>!</p>
]]></content:encoded></item></channel></rss>