<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manuel Spigolon</title>
    <description>The latest articles on DEV Community by Manuel Spigolon (@eomm).</description>
    <link>https://dev.to/eomm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F367881%2Fcb0705e8-b3aa-495b-bab9-86756ac478f0.jpeg</url>
      <title>DEV Community: Manuel Spigolon</title>
      <link>https://dev.to/eomm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eomm"/>
    <language>en</language>
    <item>
      <title>Managing Request-Scoped Data in Fastify</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Fri, 11 Apr 2025 15:29:16 +0000</pubDate>
      <link>https://dev.to/eomm/managing-request-scoped-data-in-fastify-47fp</link>
      <guid>https://dev.to/eomm/managing-request-scoped-data-in-fastify-47fp</guid>
      <description>&lt;p&gt;I was looking at the &lt;a href="https://eomm.notion.site/7a064537ee794af698684df68e215b54?v=4034009f43bd4d599a31701c4246d9fa&amp;amp;pvs=4" rel="noopener noreferrer"&gt;Fastify plugin list&lt;/a&gt;and found myself asking, &lt;em&gt;"What is this &lt;a href="https://github.com/fastify/fastify-request-context" rel="noopener noreferrer"&gt;&lt;code&gt;@fastify/request-context&lt;/code&gt; plugin&lt;/a&gt;?"&lt;/em&gt;&lt;br&gt;&lt;br&gt;
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!&lt;/p&gt;
&lt;h2&gt;
  
  
  What is AsyncLocalStorage?
&lt;/h2&gt;

&lt;p&gt;Managing state across asynchronous operations has always been a challenge in Node.js.&lt;br&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://nodejs.org/api/async_context.html#class-asynclocalstorage" rel="noopener noreferrer"&gt;&lt;strong&gt;AsyncLocalStorage&lt;/strong&gt;&lt;/a&gt;,a core module introduced in Node.js 13, comes in handy.&lt;br&gt;&lt;br&gt;
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, &lt;code&gt;AsyncLocalStorage&lt;/code&gt; allows developersto maintain &lt;strong&gt;request-scoped data&lt;/strong&gt; , meaning each incoming request gets &lt;strong&gt;its own isolated storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This feature seems to overlap with &lt;a href="https://fastify.dev/docs/latest/Reference/Decorators/" rel="noopener noreferrer"&gt;Fastify's Decorators&lt;/a&gt;, but it's not the same. Let's see why!&lt;/p&gt;
&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;The basic idea behind &lt;code&gt;AsyncLocalStorage&lt;/code&gt; is that it creates an execution context that is preservedthroughout asynchronous operations, even across &lt;code&gt;setTimeout&lt;/code&gt; or database queries.&lt;/p&gt;

&lt;p&gt;Heres a simple example with comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AsyncLocalStorage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;async_hooks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new instance of AsyncLocalStorage that will be unique per application&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appAsyncLocalStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AsyncLocalStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Simulate an incoming request every 2 seconds&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Generate a random request ID that will be unique per request&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Run the `reqHandler` function in the AsyncLocalStorage context&lt;/span&gt;
  &lt;span class="c1"&gt;// This creates the context and binds the `store` object to it&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;appAsyncLocalStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reqHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;logWithRequestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Processing request...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;logWithRequestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Finished processing.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Main business logic function&lt;/span&gt;
&lt;span class="c1"&gt;// Through `appAsyncLocalStorage.getStore()`, we can access the `store` object&lt;/span&gt;
&lt;span class="c1"&gt;// that was bound to the AsyncLocalStorage context in `reqHandler`&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logWithRequestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appAsyncLocalStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Request &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code snippet provides the &lt;code&gt;requestId&lt;/code&gt; to the &lt;code&gt;logWithRequestId&lt;/code&gt; function without passing it as a parameter!&lt;br&gt;&lt;br&gt;
It still requires access to the &lt;code&gt;appAsyncLocalStorage&lt;/code&gt; instance to retrieve the &lt;code&gt;store&lt;/code&gt; object,but with a single variable, we can access everything we need throughout the request context.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is This Important?
&lt;/h3&gt;

&lt;p&gt;Without &lt;code&gt;AsyncLocalStorage&lt;/code&gt;, you would need to manually pass the &lt;code&gt;requestId&lt;/code&gt; to every function that requires it,which can be cumbersome and error-prone.&lt;br&gt;&lt;br&gt;
With &lt;code&gt;AsyncLocalStorage&lt;/code&gt;, the context is automatically preserved throughout the request lifecycle,making it much easier to track request-specific data.&lt;/p&gt;

&lt;p&gt;Think about all the times you've had to pass a &lt;code&gt;logging&lt;/code&gt; or &lt;code&gt;config&lt;/code&gt; object to every function.&lt;br&gt;&lt;br&gt;
Or when you manually tracked the start and end of a request.&lt;br&gt;&lt;br&gt;
Or even when you implemented a tracing system to follow requests through multiple services.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;AsyncLocalStorage&lt;/code&gt;, you can forget about that spaghetti code and focus on the request context's store!&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Use the &lt;code&gt;@fastify/request-context&lt;/code&gt; Plugin
&lt;/h2&gt;

&lt;p&gt;Fastify already solves multiple problems with its decorators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It provides logging through the &lt;code&gt;request.log&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;It provides configuration through the &lt;code&gt;fastify.config&lt;/code&gt; object, thanks to the &lt;a href="https://github.com/fastify/fastify-env" rel="noopener noreferrer"&gt;&lt;code&gt;@fastify/env&lt;/code&gt; plugin&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It supports &lt;a href="https://fastify.dev/docs/latest/Reference/Hooks/#diagnostics-channel-hooks" rel="noopener noreferrer"&gt;Diagnostic Channels&lt;/a&gt; to track the request lifecycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/fastify/fastify-request-context" rel="noopener noreferrer"&gt;&lt;code&gt;@fastify/request-context&lt;/code&gt;&lt;/a&gt; plugin takes thingsfurther by offering a structured way to manage request-scoped data without the hassle of manual context management.&lt;/p&gt;
&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;After installing the plugin, you can register it in your Fastify application.&lt;br&gt;&lt;br&gt;
Lets see a real-world example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fastifyRequestContext&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fastify/request-context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fastifyRequestContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;defaultStoreValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logicStep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;longHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debugBusiness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logicStep&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Simulate some business logic&lt;/span&gt;
  &lt;span class="nx"&gt;debugBusiness&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Called external service 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Do something...&lt;/span&gt;
  &lt;span class="nx"&gt;debugBusiness&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Processed external service 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Simulate an error&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong 😱&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setErrorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debugBusiness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logicStep&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debugBusiness&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An error occurred&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have a Fastify application with a single route that simulates a complex business logic flow.&lt;br&gt;&lt;br&gt;
Thanks to the &lt;code&gt;@fastify/request-context&lt;/code&gt; plugin, we can store the &lt;code&gt;logicStep&lt;/code&gt; array in the request context and access itin every part of the application by accessing the &lt;code&gt;request&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The real power of this plugin is that you can access the &lt;code&gt;logicStep&lt;/code&gt; array inthe error handler &lt;strong&gt;without passing it through function parameters&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;Manually passing request-specific data through function arguments makes the code messy and difficult to maintain.&lt;br&gt;&lt;br&gt;
With this plugin, you can access context-specific data anywhere in the request lifecycle without modifying function signatures.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;@fastify/request-context&lt;/code&gt; vs Decorators
&lt;/h2&gt;

&lt;p&gt;Why use the &lt;code&gt;@fastify/request-context&lt;/code&gt; plugin and &lt;code&gt;AsyncLocalStorage&lt;/code&gt; instead of Fastify's Request and Reply decorators?&lt;br&gt;&lt;br&gt;
Aren't they achieving the same goal?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not exactly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To understand the difference, lets briefly look at how Fastify decorators work.&lt;br&gt;&lt;br&gt;
Fastify decorators mutate the Request and Reply prototypes, adding new properties to them.&lt;br&gt;&lt;br&gt;
Every time an HTTP request is received, Fastify creates new Request and Reply objects using these modified prototypes,automatically making decorators available.&lt;/p&gt;

&lt;p&gt;Sounds great, right? But theres a catch!&lt;/p&gt;

&lt;p&gt;If you add &lt;strong&gt;reference-type objects&lt;/strong&gt; (arrays or JSON objects) to the Request or Reply object,they will be shared across all requests.&lt;br&gt;&lt;br&gt;
If one request modifies them, it will affect all other requests!&lt;br&gt;&lt;br&gt;
This can lead to unexpected behavior and hard-to-track bugs.&lt;/p&gt;

&lt;p&gt;Due to this, the Fastify team &lt;strong&gt;discouraged&lt;/strong&gt; using reference-type objects in decoratorsstarting from &lt;a href="https://fastify.dev/docs/latest/Guides/Migration-Guide-V5/#removed-support-from-reference-types-in-decorators" rel="noopener noreferrer"&gt;v5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can still use decorators for reference-type objects, but it becomes more complex and error-prone.&lt;br&gt;&lt;br&gt;
Heres an example requiring &lt;strong&gt;two decorators&lt;/strong&gt; and a &lt;strong&gt;lazy-loading approach&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorateRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logicStepValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorateRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logicStep&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getter &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logicStepValue&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logicStepValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the &lt;code&gt;@fastify/request-context&lt;/code&gt; plugin shines.&lt;br&gt;&lt;br&gt;
It provides a &lt;strong&gt;structured&lt;/strong&gt; , &lt;strong&gt;safe&lt;/strong&gt; , and &lt;strong&gt;isolated&lt;/strong&gt; way to manage request-specificdata &lt;strong&gt;without the risk of data leakage across requests&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@fastify/request-context&lt;/code&gt; plugin offers a structured, efficient way to manage request-scoped data in Fastify applications.&lt;br&gt;&lt;br&gt;
It simplifies code, improves maintainability, enhances logging, and makes debugging significantly easier.&lt;/p&gt;

&lt;p&gt;By leveraging this plugin, you ensure that request-specific dataremains &lt;strong&gt;consistent&lt;/strong&gt; and &lt;strong&gt;accessible&lt;/strong&gt; throughout the request lifecycle &lt;strong&gt;without unnecessary complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, you might like &lt;a href="https://backend.cafe/the-fastify-book-is-out" rel="noopener noreferrer"&gt;&lt;em&gt;"Accelerating Server-Side Development with Fastify"&lt;/em&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Comment, share, and follow me on &lt;a href="https://twitter.com/ManuEomm" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>fastify</category>
      <category>isolation</category>
      <category>backend</category>
      <category>node</category>
    </item>
    <item>
      <title>Announcing the Fastify Mini Course</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Thu, 06 Mar 2025 14:35:35 +0000</pubDate>
      <link>https://dev.to/eomm/announcing-the-fastify-mini-course-49k8</link>
      <guid>https://dev.to/eomm/announcing-the-fastify-mini-course-49k8</guid>
      <description>&lt;p&gt;Dear Backend.cafe readers, I have something special for you!If you've ever wanted to master &lt;strong&gt;Fastify&lt;/strong&gt; , the lightning-fast Node.js framework, you're in for a treat.Today, Im launching &lt;strong&gt;Fastify Mini Course&lt;/strong&gt; a free resource designed to help you build high-performance APIs efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Why Fastify?
&lt;/h2&gt;

&lt;p&gt;If you're a backend developer, you've probably worked with the &lt;code&gt;http&lt;/code&gt; module. But have you considered switching to Fastify?Heres why you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazing Fast&lt;/strong&gt; : Up to &lt;strong&gt;4x faster&lt;/strong&gt; than the most popular Node.js frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Overhead&lt;/strong&gt; : Optimized for performance and scalability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema-Based Validation&lt;/strong&gt; : First-class TypeScript support and JSON schema validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Features&lt;/strong&gt; : Built-in async/await, hooks, decorators, and powerful plugin system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're an &lt;code&gt;http&lt;/code&gt; pro or new to backend development, this mini-course will help you level up your skills!&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Whats Inside the Mini Course?
&lt;/h2&gt;

&lt;p&gt;This course is &lt;strong&gt;bite-sized&lt;/strong&gt; but &lt;strong&gt;powerful&lt;/strong&gt;. Youll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to set up a Fastify project from scratch.&lt;/li&gt;
&lt;li&gt;Routing, request validation, and response serialization.&lt;/li&gt;
&lt;li&gt;Working with middleware, hooks, and decorators.&lt;/li&gt;
&lt;li&gt;Optimizing Fastify for production-ready performance.&lt;/li&gt;
&lt;li&gt;Real-world best practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The course is designed for &lt;strong&gt;maximum efficiency&lt;/strong&gt; , 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Who Is This For?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Developers who want to learn Fastify from scratch.&lt;/li&gt;
&lt;li&gt;Anyone interested in improving API performance and scalability.&lt;/li&gt;
&lt;li&gt;Engineers looking for a quick and structured way to learn Fastify.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📅 How to Get Access
&lt;/h2&gt;

&lt;p&gt;It is available &lt;strong&gt;publicly&lt;/strong&gt; at &lt;a href="https://fastifyminicourse.com/" rel="noopener noreferrer"&gt;FastifyMiniCourse.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Get Ready to Build Faster APIs!
&lt;/h2&gt;

&lt;p&gt;I cant wait for you to try it out! If you have any questions or feedback, lets discuss them in the comments.&lt;/p&gt;

&lt;p&gt;Happy coding! 🎯&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, comment, share and follow me on &lt;a href="https://x.com/ManuEomm" rel="noopener noreferrer"&gt;X&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>fastify</category>
      <category>course</category>
    </item>
    <item>
      <title>Master Node.js with the 5th Edition Cookbook</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Mon, 11 Nov 2024 14:04:30 +0000</pubDate>
      <link>https://dev.to/eomm/master-nodejs-with-the-5th-edition-cookbook-jml</link>
      <guid>https://dev.to/eomm/master-nodejs-with-the-5th-edition-cookbook-jml</guid>
      <description>&lt;p&gt;We are excited to announce that the &lt;em&gt;Node.js Cookbook, 5th Edition&lt;/em&gt; is set to be released on &lt;strong&gt;November 15th, 2024&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;This latest edition, authored by &lt;a href="https://github.com/BethGriggs" rel="noopener noreferrer"&gt;&lt;strong&gt;Bethany Griggs&lt;/strong&gt;&lt;/a&gt;, is the ultimate guide for developers who want to master the Node.js v22 API and build scalable, high-performance applications.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Node.js Cookbook&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;As a special contribution, &lt;a href="https://github.com/Eomm/" rel="noopener noreferrer"&gt;&lt;strong&gt;Manuel Spigolon&lt;/strong&gt;&lt;/a&gt; had the pleasure of writing a chapter dedicated to the latest &lt;strong&gt;Fastify v5&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;p&gt;If you're eager to dive into Node.js or explore Fastify v5, you can &lt;strong&gt;preorder the book today&lt;/strong&gt; at &lt;a href="https://packt.link/0mNGV" rel="noopener noreferrer"&gt;https://packt.link/0mNGV&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dont miss it!&lt;/p&gt;

</description>
      <category>node</category>
      <category>books</category>
      <category>fastify</category>
    </item>
    <item>
      <title>Resume data replication in Postgres and Node.js</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Mon, 11 Nov 2024 14:03:59 +0000</pubDate>
      <link>https://dev.to/eomm/resume-data-replication-in-postgres-and-nodejs-1739</link>
      <guid>https://dev.to/eomm/resume-data-replication-in-postgres-and-nodejs-1739</guid>
      <description>&lt;h2&gt;
  
  
  How to resume replication from the point where the Node.js application was stopped
&lt;/h2&gt;

&lt;p&gt;This article is a continuation of &lt;a href="https://backend.cafe/real-time-data-replication-in-postgres-and-nodejs" rel="noopener noreferrer"&gt;Real-time data Replication in Postgres and Node.js&lt;/a&gt;. Before reading this article, I recommend you read the previous one because it provides essential context to the points I cover.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In this article, I discuss how to resume replication from the last point where the Node.js application stopped&lt;br&gt;
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.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a replication slot
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;postgres&lt;/code&gt; package we used in the previous article automatically created a replication slot for us, but it was not persistent, it was a &lt;a href="https://www.postgresql.org/docs/16/view-pg-replication-slots.html" rel="noopener noreferrer"&gt;&lt;code&gt;TEMPORARY&lt;/code&gt;&lt;/a&gt; replication slot that was removed when the subscriber disconnected.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;setup-replication.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foopsw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createReplicationSlotIfNotExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createReplicationSlotIfNotExists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slotName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM pg_replication_slots WHERE slot_name = $1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;slotName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newSlot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM pg_create_logical_replication_slot($1, 'pgoutput')&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;slotName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Created replication slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newSlot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Slot already exists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using the &lt;a href="https://www.postgresql.org/docs/16/view-pg-replication-slots.html" rel="noopener noreferrer"&gt;&lt;code&gt;pg_replication_slots&lt;/code&gt;&lt;/a&gt; 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 &lt;a href="https://www.postgresql.org/docs/16/functions-admin.html#FUNCTIONS-REPLICATION" rel="noopener noreferrer"&gt;&lt;code&gt;pg_create_logical_replication_slot&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;Note that we specified the &lt;a href="https://www.postgresql.org/docs/16/protocol-logical-replication.html" rel="noopener noreferrer"&gt;&lt;code&gt;pgoutput&lt;/code&gt; plugin&lt;/a&gt; in the function to decode the changes in the replication slot. This is the default plugin for logical replication, and it ships with Postgres.&lt;br&gt;
Be aware that there are other plugins, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.postgresql.org/docs/16/test-decoding.html" rel="noopener noreferrer"&gt;&lt;code&gt;test_decoding&lt;/code&gt; plugin&lt;/a&gt; is the simplest plugin that ships with Postgres to start building your own custom plugin.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://packages.ubuntu.com/noble/postgresql-16-wal2json" rel="noopener noreferrer"&gt;&lt;code&gt;wal2json&lt;/code&gt;&lt;/a&gt;, which must be installed separately in the Postgres database, allowing you to use them in the &lt;code&gt;pg_create_logical_replication_slot&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that each plugin has its own advantages and disadvantages, so choose the one that best fits your use case.&lt;br&gt;
The biggest difference if you try to use &lt;code&gt;test_decoding&lt;/code&gt; versus &lt;code&gt;pgoutput&lt;/code&gt; is that the former does not accept a publication name&lt;br&gt;
as a parameter while the &lt;a href="https://github.com/postgres/postgres/blob/3c469a939cf1cc95b136653e7c6e27e472dc0472/src/backend/replication/pgoutput/pgoutput.c#L449-L452" rel="noopener noreferrer"&gt;latter does&lt;/a&gt;. This means that you can use &lt;code&gt;pgoutput&lt;/code&gt; to filter the changes you want to replicate, while &lt;code&gt;test_decoding&lt;/code&gt; will replicate all changes in the database without filtering them!&lt;/p&gt;

&lt;p&gt;Now, run the &lt;code&gt;setup-replication.js&lt;/code&gt; file to create a replication slot!&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring the consumer
&lt;/h2&gt;

&lt;p&gt;In the previous article, we already created the &lt;code&gt;setup-consumer.js&lt;/code&gt; that creates the publications that&lt;br&gt;
our application is interested in. So, we can reuse the same file and just run it — if we haven't already.&lt;/p&gt;

&lt;p&gt;As a reminder: you will first need to start the Postgres server and create the &lt;code&gt;foo&lt;/code&gt; database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resuming the replication
&lt;/h2&gt;

&lt;p&gt;We are ready to create a new &lt;code&gt;consumer-resume.js&lt;/code&gt; file that will resume replication from the last point where the Node.js application was stopped, so let's jump into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LogicalReplicationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PgoutputPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg-logical-replication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LogicalReplicationService&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foopsw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;acknowledge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lsn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) Received insert: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lsn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) Received log: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acknowledge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lsn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventDecoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PgoutputPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Get a complete list of available options at:&lt;/span&gt;
  &lt;span class="c1"&gt;// https://www.postgresql.org/docs/16/protocol-logical-replication.html&lt;/span&gt;
  &lt;span class="na"&gt;protoVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publicationNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_odd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_update_only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Listening for changes...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stopping client...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventDecoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_slot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, we are using the &lt;a href="https://www.npmjs.com/package/pg-logical-replication" rel="noopener noreferrer"&gt;&lt;code&gt;pg-logical-replication&lt;/code&gt;&lt;/a&gt; package to demonstrate the resuming of replication. Its low-level API provides us more control over the replication process,&lt;br&gt;
otherwise we would not be able to configure the Plugin to receive only the changes we are interested in.&lt;/p&gt;

&lt;p&gt;The code can be explained as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We are creating a new &lt;code&gt;LogicalReplicationService&lt;/code&gt; instance and passing the connection options to it. Note that we are setting the &lt;code&gt;acknowledge.auto&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt; to manually acknowledge the changes; otherwise, they would be automatically acknowledged. By setting this option to &lt;code&gt;false&lt;/code&gt;, we gain even more control over the process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We are listening to the &lt;code&gt;data&lt;/code&gt; event to receive changes from the replication slot.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At this point, you should process the &lt;code&gt;log&lt;/code&gt; and apply your business logic. In this case, we're just logging the changes to the console.&lt;/li&gt;
&lt;li&gt;After processing the changes, you must acknowledge them using the &lt;code&gt;acknowledge&lt;/code&gt; method; otherwise, the slot will not advance. The &lt;code&gt;lsn&lt;/code&gt; (&lt;strong&gt;Log Sequence Number&lt;/strong&gt;) is the unique identifier for each change in the database and is used to track changes in the replication slot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are creating a new &lt;code&gt;PgoutputPlugin&lt;/code&gt; instance and passing it to the &lt;code&gt;subscribe&lt;/code&gt; method to establish a connection with the replication slot.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To start the application, run the &lt;code&gt;node consumer-resume.js&lt;/code&gt; file, and it will begin receiving changes from the replication slot. If we did all the steps correctly, you can start the &lt;code&gt;node producer.js&lt;/code&gt; file that we&lt;br&gt;
wrote in the previous article to produce changes in the database and see the changes in the consumer application.&lt;/p&gt;

&lt;p&gt;If you stop the consumer application by pressing &lt;code&gt;Ctrl+C&lt;/code&gt;, the replication will stop, and the slot will not move forward. However, if you start the &lt;code&gt;consumer-resume.js&lt;/code&gt; application again, it will resume replication from the last point where it was stopped! 🎉&lt;/p&gt;

&lt;p&gt;Moreover, we can see that the output shows only the changes from the &lt;code&gt;foo_odd&lt;/code&gt; and &lt;code&gt;foo_update_only&lt;/code&gt; publications, which we configured in the &lt;code&gt;PgoutputPlugin&lt;/code&gt; instance so we will see updates and inserts with odd &lt;code&gt;id&lt;/code&gt; numbers only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0/15648E0&lt;span class="o"&gt;)&lt;/span&gt; Received insert: public.foo 18
0/15649A0&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/1564AF0&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/1564B80&lt;span class="o"&gt;)&lt;/span&gt; Received insert: public.foo 20
0/1564C40&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/1564D90&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/1564E20&lt;span class="o"&gt;)&lt;/span&gt; Received insert: public.foo 22
0/1564EE0&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/1565030&lt;span class="o"&gt;)&lt;/span&gt; Received log: public.foo update
0/15650C0&lt;span class="o"&gt;)&lt;/span&gt; Received insert: public.foo 24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we discussed how to resume replication from the last point where the Node.js application was stopped.&lt;/p&gt;

&lt;p&gt;We created a persistent replication slot in the Postgres database and used the &lt;code&gt;pg-logical-replication&lt;/code&gt; package to demonstrate resuming replication. This ensures that our application doesn't lose data produced by other microservices or applications during downtime.&lt;/p&gt;

&lt;p&gt;In doing so, we did not change the &lt;code&gt;producer.js&lt;/code&gt; 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&lt;br&gt;
replication slot and the new consumer.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article and learned something new!&lt;br&gt;
Does it deserve a comment and a share? 🚀&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>node</category>
      <category>replicaset</category>
    </item>
    <item>
      <title>Real-time data replication in Postgres and Node.js</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Wed, 18 Sep 2024 14:47:24 +0000</pubDate>
      <link>https://dev.to/eomm/real-time-data-replication-in-postgres-and-nodejs-2cmn</link>
      <guid>https://dev.to/eomm/real-time-data-replication-in-postgres-and-nodejs-2cmn</guid>
      <description>&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keeping different databases in sync, which is a typical scenario when a company acquires another company and needs to merge the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keeping an application's in-memory cache in sync with the database to improve the performance of read queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For high availability and disaster recovery scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;&lt;br&gt;
&lt;em&gt;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.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction to Postgres replication management
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is a powerful open-source relational database system known for its robust features and extensibility. One of its standout capabilities is &lt;strong&gt;replication management&lt;/strong&gt; , allowing you to replicate data from one database to another in real-time. This feature is invaluable to organisations that require characteristics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;High availability: the primary database fails, and a standby server takes over seamlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load balancing: distributing read queries across multiple replicas to improve performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data distribution: synchronising data across geographically distributed systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Postgres offers various mechanisms to support the listed scenarios where each solution has its strengths and use cases. To list a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;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!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete list of replication mechanisms can be found in the &lt;a href="https://www.postgresql.org/docs/16/different-replication-solutions.html" rel="noopener noreferrer"&gt;Postgres documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll focus on &lt;a href="https://www.postgresql.org/docs/16/logical-replication.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Logical Replication&lt;/strong&gt;&lt;/a&gt; mechanism.&lt;/p&gt;
&lt;h3&gt;
  
  
  The WAL file
&lt;/h3&gt;

&lt;p&gt;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 &lt;a href="https://www.postgresql.org/docs/16/wal-reliability.html" rel="noopener noreferrer"&gt;durability&lt;/a&gt; in case of a crash or disk failure.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://www.postgresql.org/docs/16/runtime-config-wal.html#GUC-WAL-LEVEL" rel="noopener noreferrer"&gt;&lt;code&gt;wal_level&lt;/code&gt;&lt;/a&gt; configuration parameter. This parameter defines how much information is written to the WAL file. The possible values are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;minimal&lt;/code&gt;: only the information needed to recover from a crash is written to the WAL file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;replica&lt;/code&gt;: the default value, it writes enough information to support WAL archiving and replication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;logical&lt;/code&gt;: it stores the same information as &lt;code&gt;replica&lt;/code&gt; and additional information needed for logical replication, so this configuration requires more disk space.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Developing a real-time data replication system with Node.js
&lt;/h2&gt;

&lt;p&gt;To develop a real-time data replication system with Node.js, we must follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Initialise the Postgres database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a data producer we want to replicate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a data consumer that replicates the data through the Postgres replication mechanism.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's start!&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up Postgres
&lt;/h3&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; demo-postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foopsw &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:16 &lt;span class="se"&gt;\&lt;/span&gt;
  postgres &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"wal_level=logical"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start a Postgres instance with logical replication enabled, and you can connect to it using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; demo-postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, from the Postgres shell, we can create a database and a table to work with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;col1&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;col2&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check if the table was created successfully, the following command lists the user's tables in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating a data producer
&lt;/h3&gt;

&lt;p&gt;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 &lt;code&gt;foo&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Let's create a &lt;code&gt;producer.js&lt;/code&gt; file and implement the following logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It connects to the Postgres database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every second, it inserts a new row into the &lt;code&gt;foo&lt;/code&gt; table or updates the last inserted row alternatively.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It listens for a &lt;code&gt;SIGINT&lt;/code&gt; signal to gracefully close the connection.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// ℹ️ npm install pg&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foopsw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakeUserInteraction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT INTO foo (col1, col2) VALUES ($1, $2) RETURNING id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;lastId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Inserted new row: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UPDATE foo SET col1 = $1, col2 = $2 WHERE id = $3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world!!!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Updated row &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeUserInteraction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run this code the &lt;code&gt;node producer.js&lt;/code&gt; command is enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a data consumer
&lt;/h3&gt;

&lt;p&gt;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 &lt;a href="https://www.youtube.com/watch?v=3Z4Hhh5EnLA" rel="noopener noreferrer"&gt;video tutorials&lt;/a&gt; for this use case).&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;foo&lt;/code&gt; table using the logical replication mechanism.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up the data consumer
&lt;/h4&gt;

&lt;p&gt;To set up the data consumer, we must introduce some new Postgres concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/16/logical-replication-publication.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Publication&lt;/strong&gt;&lt;/a&gt;: 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/16/logical-replication-subscription.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Subscription&lt;/strong&gt;&lt;/a&gt;: 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 &lt;code&gt;foo&lt;/code&gt; table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/16/warm-standby.html#STREAMING-REPLICATION-SLOTS" rel="noopener noreferrer"&gt;&lt;strong&gt;Replication slot&lt;/strong&gt;&lt;/a&gt;: 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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 &lt;code&gt;setup-consumer.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foopsw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPublicationIfNotExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_even&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TABLE foo WHERE (id % 2 = 0);&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPublicationIfNotExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_update_only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TABLE foo WITH (publish = 'update');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPublicationIfNotExists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM pg_publication WHERE pubname = $1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CREATE PUBLICATION &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; FOR &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Created publication &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ALTER PUBLICATION &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SET &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Updated publication &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pubName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code shows how to create and update a publication. When a publication is created, it specifies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which tables and columns are included in the replication process?&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If no columns are specified, all columns are included.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is possible to list multiple tables or set &lt;code&gt;FOR ALL TABLES;&lt;/code&gt; to include all tables in the replication process.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Which operations are included in the replication process?&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The default is to include all operations (inserts, updates, deletes).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is possible to specify only inserts, only updates, or only deletes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have created two publications in the code snippet above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;foo_even&lt;/code&gt;: it includes only rows where the &lt;code&gt;id&lt;/code&gt; is an even number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;foo_update_only&lt;/code&gt;: it includes only updates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As shown, the publications are very flexible and can be tailored to specific use cases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Subscribing to changes
&lt;/h4&gt;

&lt;p&gt;Finally, we are ready to create the &lt;code&gt;consumer.js&lt;/code&gt; file to listen for changes in the &lt;code&gt;foo&lt;/code&gt; table. To do so, we will use the &lt;a href="https://www.npmjs.com/package/postgres" rel="noopener noreferrer"&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/a&gt; module because it provides a simple way to consume the logical replication data changes or we should implement the &lt;a href="https://www.postgresql.org/docs/16/protocol-logical-replication.html" rel="noopener noreferrer"&gt;logical replication protocol&lt;/a&gt; ourselves. Alternatively, you can use the &lt;a href="https://www.npmjs.com/package/pg-logical-replication" rel="noopener noreferrer"&gt;&lt;code&gt;pg-logical-replication&lt;/code&gt;&lt;/a&gt; library it depends on your preference.&lt;/p&gt;

&lt;p&gt;To create the &lt;code&gt;consumer.js&lt;/code&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postgres&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foopsw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_even&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo_update_only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can subscribe to the changes happening in the &lt;code&gt;foo&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*:foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;eventPattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;old&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;row&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onConnect&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Callback on initial connect and potential reconnects&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connected to the publication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the &lt;code&gt;node consumer.js&lt;/code&gt; command, we will see the &lt;code&gt;console.log&lt;/code&gt; output every time the &lt;code&gt;producer.js&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Insert a row with an even ID in the &lt;code&gt;foo&lt;/code&gt; table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update a row in the &lt;code&gt;foo&lt;/code&gt; table.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the &lt;a href="https://github.com/porsager/postgres?tab=readme-ov-file#subscribe-pattern" rel="noopener noreferrer"&gt;&lt;code&gt;eventPattern&lt;/code&gt;&lt;/a&gt; is just a feature of the &lt;code&gt;postgres&lt;/code&gt; 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 &lt;code&gt;setup-consumer.js&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;So far we did not create the Replication Slot, but the &lt;code&gt;postgres&lt;/code&gt; module will do it for us by calling the &lt;a href="https://www.postgresql.org/docs/16/protocol-replication.html#PROTOCOL-REPLICATION-CREATE-REPLICATION-SLOT" rel="noopener noreferrer"&gt;&lt;code&gt;CREATE_REPLICATION_SLOT&lt;/code&gt;&lt;/a&gt; command creating a &lt;a href="https://github.com/porsager/postgres/blob/cc688c642fc98c4338523d3e281e03bf0c3417b8/src/subscribe.js#L85" rel="noopener noreferrer"&gt;&lt;code&gt;TEMPORARY&lt;/code&gt;&lt;/a&gt; slot. Every subscription needs a Replication Slot to track the changes that they are consuming.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;postgres&lt;/code&gt; module creates a temporary replication slot that is automatically deleted when the connection is closed.&lt;/p&gt;

&lt;p&gt;Now our Node.js application is ready to listen for changes in the &lt;code&gt;foo&lt;/code&gt; table and replicate them in real-time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://www.npmjs.com/package/pg-logical-replication#2-usage" rel="noopener noreferrer"&gt;&lt;code&gt;pg-logical-replication&lt;/code&gt;&lt;/a&gt; module's API, but this will be the topic of another blog post!&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=WXKxwl9k5Q8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=WXKxwl9k5Q8&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=3Z4Hhh5EnLA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=3Z4Hhh5EnLA&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=E6W-ZWVRAHU" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=E6W-ZWVRAHU&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comment and share if you enjoyed this article!&lt;/p&gt;

</description>
      <category>node</category>
      <category>postgres</category>
      <category>fastify</category>
    </item>
    <item>
      <title>How to implement video streaming with Fastify</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Sat, 31 Aug 2024 05:00:10 +0000</pubDate>
      <link>https://dev.to/eomm/how-to-implement-video-streaming-with-fastify-4k1d</link>
      <guid>https://dev.to/eomm/how-to-implement-video-streaming-with-fastify-4k1d</guid>
      <description>&lt;p&gt;In April 2021, &lt;a href="https://x.com/ManuEomm/status/1385694984880406528" rel="noopener noreferrer"&gt;I tweeted&lt;/a&gt; about a quick experiment where I streamed a large GoPro video using Fastify.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;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 fiance 😄&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a new directory for the project
mkdir fastify-video-streaming
cd fastify-video-streaming

# Initialize a new Node.js project with ES6 module support
npm init es6 --yes

# Install Fastify and necessary plugins
npm install fastify@4 fastify-range@1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the project initialised and dependencies installed, we're ready to start building the video streaming server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting ready to stream
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;First, create a new file named &lt;code&gt;app.js&lt;/code&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the Fastify application with logging enabled&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Set up a route to serve the HTML page&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Serve the index.html file from the project directory&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Start the server and listen on port 8080&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create an &lt;code&gt;index.html&lt;/code&gt; file in the same directory to define the structure of our frontend and add the following &lt;strong&gt;basic&lt;/strong&gt; code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Video Streaming with Fastify&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Fastify Video Streaming&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/video-streaming"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"video/mp4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Your browser does not support the video tag.
    &lt;span class="nt"&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple HTML page includes a video element that will request the video stream from our server via the &lt;code&gt;/video-streaming&lt;/code&gt; route, which we'll implement next.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;By running the &lt;code&gt;node --watch app.js&lt;/code&gt; command in your terminal, you can start the server and access the page at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/a&gt; in your browser. You should see a basic page with a video player ready to stream content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming video content
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding range requests
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Range&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;The range request feature is defined by &lt;a href="https://datatracker.ietf.org/doc/html/rfc7233" rel="noopener noreferrer"&gt;RFC 7233&lt;/a&gt;. When a browser sends a request for a video, it typically includes a &lt;code&gt;Range&lt;/code&gt; header specifying the byte range it wants.&lt;/p&gt;

&lt;p&gt;An example of a &lt;code&gt;Range&lt;/code&gt; header might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Range: bytes=0-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;206 Partial Content&lt;/code&gt; status and includes the requested byte range in the &lt;code&gt;Content-Range&lt;/code&gt; header. Here's an example of the response headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Range: bytes 0-1000000/2257069623
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This header indicates that the server is sending bytes 0 to 1 MB of a total of 2.257.069.623 (&amp;gt;2GB!) bytes in the video file.&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Range: bytes=1000001-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;Range&lt;/code&gt; 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 &lt;a href="https://stackoverflow.com/q/21765555/3309466" rel="noopener noreferrer"&gt;interesting topic on StackOverflow&lt;/a&gt;, and I am mentioning it because it can be an interesting exercise for the mind to compare and contrast different ideas between developers!&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the &lt;code&gt;/video-streaming&lt;/code&gt; route
&lt;/h3&gt;

&lt;p&gt;Lets add the &lt;code&gt;/video-streaming&lt;/code&gt; route to our Fastify application to handle these range requests.&lt;/p&gt;

&lt;p&gt;First, we must use the &lt;code&gt;fastify-range&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fastifyRange&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify-range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fastifyRange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can focus on the &lt;code&gt;/video-streaming&lt;/code&gt; route implementation. Note that the route name was defined in the &lt;code&gt;src&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag in the &lt;code&gt;index.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/video-streaming&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/your/video.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoPath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;

  &lt;span class="c1"&gt;// Extract the range from the request headers&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If no valid range is found, throw a 416 error&lt;/span&gt;
    &lt;span class="c1"&gt;// as indicated by the RFC 7233&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Range Not Satisfiable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;416&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Handle only the first range requested&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;singleRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ranges&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;// Define the size of the chunk to send&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e6&lt;/span&gt; &lt;span class="c1"&gt;// 1MB&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;singleRange&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;videoSize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the appropriate headers for range requests&lt;/span&gt;
  &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept-Ranges&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bytes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`bytes &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;videoSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentLength&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Send a 206 Partial Content status code&lt;/span&gt;
  &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;206&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Stream the requested chunk of the video file&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extracting the range:&lt;/strong&gt; When the browser requests the video, it sends a &lt;code&gt;Range&lt;/code&gt; header indicating the portion of the file it wants to download. We extract this range using the &lt;code&gt;request.range(videoSize)&lt;/code&gt; method, which is provided by the &lt;code&gt;fastify-range&lt;/code&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validating the range:&lt;/strong&gt; If no valid range is provided or the range is unsatisfiable, the server responds with a &lt;code&gt;416 Range Not Satisfiable&lt;/code&gt; error. This indicates that the server cannot fulfil the request as specified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunk size and byte ranges:&lt;/strong&gt; We calculate the size of the chunk to send based on the requested range. In this case, weve 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 &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; variables define the exact byte range that will be streamed back to the client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Setting headers:&lt;/strong&gt; The server responds with a &lt;code&gt;206 Partial Content&lt;/code&gt; status and includes headers like &lt;code&gt;Content-Range&lt;/code&gt; and &lt;code&gt;Content-Length&lt;/code&gt; to inform the client of the specific byte range being sent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming the video:&lt;/strong&gt; Finally, the server streams the requested portion of the video file using &lt;code&gt;fs.createReadStream(videoPath, { start, end })&lt;/code&gt;. This allows the video player in the browser to play the video while additional portions are requested as needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The HTML &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag in our &lt;code&gt;index.html&lt;/code&gt; 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 &lt;code&gt;Range&lt;/code&gt; header, prompting the server to deliver the corresponding video segment.&lt;/p&gt;

&lt;p&gt;This mechanism ensures smooth playback and efficient use of bandwidth, as only the necessary parts of the video are loaded and played.&lt;/p&gt;

&lt;p&gt;By implementing this route, weve enabled our server to stream large video files efficiently, providing users with the ability to navigate through the video timeline seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Fblog-posts%2Fblob%2Fmaster%2Fbonus%2Fvideo-streaming%2Ffastify-video-streaming.gif%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Fblog-posts%2Fblob%2Fmaster%2Fbonus%2Fvideo-streaming%2Ffastify-video-streaming.gif%3Fraw%3Dtrue" alt="Video example" width="244" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://github.com/Eomm/blog-posts/tree/HEAD/bonus/video-streaming/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, comment, share and follow me on &lt;a href="https://twitter.com/ManuEomm" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>fastify</category>
      <category>videostreaming</category>
      <category>backend</category>
    </item>
    <item>
      <title>Streaming PostgreSQL data with Fastify</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Tue, 20 Aug 2024 14:58:40 +0000</pubDate>
      <link>https://dev.to/eomm/streaming-postgresql-data-with-fastify-4lne</link>
      <guid>https://dev.to/eomm/streaming-postgresql-data-with-fastify-4lne</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Clients can then query the API and get the data they need to render the visualization.&lt;/p&gt;

&lt;p&gt;But what happens when the dataset is enormous? And what if the query to access the data is &lt;strong&gt;complex&lt;/strong&gt; and &lt;strong&gt;slow&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So, we will see how to stream a massive amount of data from a PostgreSQL database to a Reactjs client!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the dataset
&lt;/h2&gt;

&lt;p&gt;I will go fast here because this is not the main topic of this article, and the code is available on &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will create two tables that represent our huge dataset:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;desks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200,000&lt;/td&gt;
&lt;td&gt;200 thousand desks in a building&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;items&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10,000,000&lt;/td&gt;
&lt;td&gt;10 million items on the desks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The query we are going to analyze is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;row_number&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;
  &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;desks&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;desks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;desk_id&lt;/span&gt;
  &lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads all the items and the desks they are on and returns the result with a row number.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;pg_sleep(1)&lt;/code&gt; forces PostgreSQL to wait for 1 second before returning the result.&lt;/p&gt;

&lt;p&gt;Now we can start the PostgreSQL server with a quick Docker command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="nt"&gt;--name&lt;/span&gt; fastify-postgres &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="nt"&gt;-d&lt;/span&gt; postgres:15-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can seed the database with the tables and the data with the &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/" rel="noopener noreferrer"&gt;&lt;code&gt;node seed.js&lt;/code&gt; script on GitHub&lt;/a&gt;. It will take a few minutes to complete, but we will have a huge dataset to play with at the end!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the API
&lt;/h2&gt;

&lt;p&gt;We are going to use &lt;a href="https://www.fastify.dev/" rel="noopener noreferrer"&gt;Fastify&lt;/a&gt; to build the API, it has everything we need to build a fast and scalable API in no time 🎉.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;So we need to stream the result of the query to the client by using different techniques:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batching&lt;/strong&gt; : we can run the query in batches and return the result in chunks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt; : we can stream the result of the query to the client as soon as we get the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Batching approach can be implemented using the SQL language's &lt;code&gt;LIMIT&lt;/code&gt; and &lt;code&gt;OFFSET&lt;/code&gt; clauses. So we don't need any external library to implement it.&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/pg-cursor" rel="noopener noreferrer"&gt;&lt;code&gt;pg-cursor&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/pg-query-stream" rel="noopener noreferrer"&gt;&lt;code&gt;pg-query-stream&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both libraries are great, but the &lt;code&gt;pg-query-stream&lt;/code&gt; has a better API, so we will use it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I tested both &lt;code&gt;pg-&lt;/code&gt; libraries before noticing that &lt;code&gt;pg-query-stream&lt;/code&gt; uses &lt;code&gt;pg-cursor&lt;/code&gt; under the hood 😅 The performance of the two libraries is the same, so we will discuss &lt;code&gt;pg-query-stream&lt;/code&gt; only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we know what to do, we can start writing the code!&lt;/p&gt;

&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you want to learn more about Fastify, you may find the &lt;a href="https://backend.cafe/fastify-v4-book" rel="noopener noreferrer"&gt;Fastify book&lt;/a&gt; helpful!&lt;/p&gt;

&lt;p&gt;So, let's start creating a new project from scratch with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the project&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;fastify-postgres-high-volume
&lt;span class="nb"&gt;cd &lt;/span&gt;fastify-postgres-high-volume
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install Fastify&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;fastify @fastify/postgres

&lt;span class="c"&gt;# Install the PostgreSQL driver and utilities&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;pg pg-query-stream JSONStream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;app.js&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;

&lt;span class="c1"&gt;// Create the Fastify server&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// A simple route to serve the Reactjs application&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;serveUi&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Register the PostgreSQL plugin to connect to the database&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@fastify/postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Register some plugins to implement the API - we will see them later&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Start the server&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is time to scaffold the &lt;code&gt;lib/batch.js&lt;/code&gt; plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queryBatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;queryBatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notImplemented&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not implemented&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;notImplemented&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;501&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;notImplemented&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's scaffold the &lt;code&gt;lib/stream.js&lt;/code&gt; plugin too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queryStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;queryStream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notImplemented&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not implemented&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;notImplemented&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;501&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;notImplemented&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to implement the two plugins in the next sections.&lt;/p&gt;

&lt;p&gt;The last thing we need to do is to create the &lt;code&gt;index.html&lt;/code&gt; 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 &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/index.html" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. 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.&lt;/p&gt;

&lt;p&gt;At this point, we can start the server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Node.js Tip&lt;br&gt;&lt;br&gt;
If you are running Node.js 20 or newer, you can use the &lt;code&gt;node --watch app.js&lt;/code&gt; flag to reload the server when the code changes! It doesn't require any external library and is very useful during development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the server is up and running, we can open the browser and navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt; and we should see the &lt;code&gt;index.html&lt;/code&gt; application!&lt;/p&gt;

&lt;p&gt;We can now implement the API's business logic!&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the Batching approach
&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;limit&lt;/code&gt;: the number of rows to return in each chunk.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;offset&lt;/code&gt;: the number of rows to skip before returning the batch result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can get these two values in the &lt;code&gt;request.query&lt;/code&gt; object by adding a JSON schema to the route that validates the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;queryBatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;queryBatch&lt;/code&gt; implementation will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;queryBatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the input from the client&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;

  &lt;span class="c1"&gt;// The slow query we want to run&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slowQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    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;
  `&lt;/span&gt;

  &lt;span class="c1"&gt;// Run the query and return the result&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slowQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the client calls the &lt;code&gt;/api/batch&lt;/code&gt; route, the server will run the &lt;code&gt;slowQuery&lt;/code&gt; and return the result.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;this.pg&lt;/code&gt; object is an Application Decorator injected by the &lt;code&gt;@fastify/postgres&lt;/code&gt; plugin, and it is a PostgreSQL pool!&lt;/p&gt;

&lt;p&gt;We can now test the API by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:8080/api/batch?offset=0&amp;amp;limit=10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server should return the first 10,000 rows of the result of the &lt;code&gt;slowQuery&lt;/code&gt;! Note that the response will pop up in the terminal after 1 second at the latest 🐌.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the Streaming approach
&lt;/h3&gt;

&lt;p&gt;The Streaming approach &lt;strong&gt;could&lt;/strong&gt; be more complex to implement. However, thanks to the &lt;a href="https://www.npmjs.com/package/pg-query-stream" rel="noopener noreferrer"&gt;&lt;code&gt;pg-query-stream&lt;/code&gt;&lt;/a&gt; library, it is not .&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;limit&lt;/code&gt; only because we are going to stream the result and don't need to skip anything.&lt;/p&gt;

&lt;p&gt;We can read the &lt;code&gt;limit&lt;/code&gt; input from the &lt;code&gt;request.query&lt;/code&gt; object by adding a JSON schema to the route that validates the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;queryStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can implement the &lt;code&gt;queryStream&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QueryStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg-query-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;JSONStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JSONStream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;queryStream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the PostgreSQL client from the pool&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// The slow query we want to run&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slowQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    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;
  `&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a new stream that runs the query&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slowQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Run the query and return the stream&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSONStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's analyze the code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We create a new PostgreSQL client by calling &lt;code&gt;this.pg.connect()&lt;/code&gt; that returns an available &lt;code&gt;client&lt;/code&gt; from the pool.&lt;/li&gt;
&lt;li&gt;We create a new &lt;code&gt;QueryStream&lt;/code&gt; that will run the same &lt;code&gt;slowQuery&lt;/code&gt; as the batch implementation except for the &lt;code&gt;OFFSET&lt;/code&gt; clause:

&lt;ul&gt;
&lt;li&gt;It is important to note that we need to set the &lt;code&gt;highWaterMark&lt;/code&gt; option to read a good amount of rows from the database. The &lt;a href="https://nodejs.org/api/stream.html#streamgetdefaulthighwatermarkobjectmode" rel="noopener noreferrer"&gt;default value is 16&lt;/a&gt;, which is too low for our use case!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We create a new &lt;code&gt;stream&lt;/code&gt; by calling &lt;code&gt;client.query(query)&lt;/code&gt; that will execute the query.&lt;/li&gt;
&lt;li&gt;We must remember to listen for the &lt;code&gt;end&lt;/code&gt; event of the &lt;code&gt;stream&lt;/code&gt; to release the &lt;code&gt;client&lt;/code&gt; back to the pool.&lt;/li&gt;
&lt;li&gt;We return the &lt;code&gt;stream&lt;/code&gt; piped to a &lt;code&gt;JSONStream&lt;/code&gt; that will convert the result to a JSON string. Fastify will automatically manage the stream and send the result to the client!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can now test the API by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:8080/api/stream?limit=10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server should return the first 10,000 rows of the result of the &lt;code&gt;slowQuery&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;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! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the client
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/postgres/index.html" rel="noopener noreferrer"&gt;Reactjs application&lt;/a&gt; I have created before.&lt;/p&gt;

&lt;p&gt;It is a simple application with the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An input to set globally for the application how many rows we must fetch from the API calls.&lt;/li&gt;
&lt;li&gt;A button to call the &lt;code&gt;/api/batch&lt;/code&gt; route and show the time it takes to get the result.

&lt;ul&gt;
&lt;li&gt;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!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A button to call the &lt;code&gt;/api/stream&lt;/code&gt; route and show the time it takes to get the result.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here is a screenshot of the application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-idle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-idle.png" alt="the application UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's press the buttons and see what happens!&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;If we press the buttons with the default values, we will get the following results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-results.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-results.png" alt="the application UI results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It must be noted that the results include the time it takes to parse the data in the browser.&lt;/p&gt;

&lt;p&gt;Before analyzing the results, let's try to play with the inputs to see how the results change.&lt;/p&gt;

&lt;p&gt;What happens if we increase the batch size to 350,000?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-big-batch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FEomm%2Ffastify-discord-bot-demo%2Fraw%2F8a1272454722a54bf3b62764d2ea3ccab54daabb%2Fposts%2Fassets%2Fpg-big-batch.png" alt="the application UI results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The batch approach is much faster than before, and it beats the streaming method!&lt;/p&gt;

&lt;p&gt;Let's analyze the results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Total Rows&lt;/th&gt;
&lt;th&gt;Batch Size&lt;/th&gt;
&lt;th&gt;Batch Result Time (sec)&lt;/th&gt;
&lt;th&gt;Stream Result Time (sec)&lt;/th&gt;
&lt;th&gt;Number of Batches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;~14&lt;/td&gt;
&lt;td&gt;~7 🥇&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;350,000&lt;/td&gt;
&lt;td&gt;~5 🥇&lt;/td&gt;
&lt;td&gt;~7&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;350,000&lt;/td&gt;
&lt;td&gt;~8 🥇&lt;/td&gt;
&lt;td&gt;~15&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3,000,000&lt;/td&gt;
&lt;td&gt;350,000&lt;/td&gt;
&lt;td&gt;~30 🥈&lt;/td&gt;
&lt;td&gt;~30 🥈&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;td&gt;350,000&lt;/td&gt;
&lt;td&gt;~36&lt;/td&gt;
&lt;td&gt;~34 🥇&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5,000,000&lt;/td&gt;
&lt;td&gt;350,000&lt;/td&gt;
&lt;td&gt;~63&lt;/td&gt;
&lt;td&gt;~53 🥇&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The results are exciting! The streaming approach is faster than the batch approach when the number of batches (calculated by &lt;code&gt;totalRows / batchSize&lt;/code&gt;) is higher or equal to 10.&lt;/p&gt;

&lt;p&gt;Is this a general rule? Let's answer this question by doing some investigating!&lt;/p&gt;

&lt;p&gt;Watch this video taken from the Chrome DevTools's Network tab about the 2nd test result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Eomm/blog-posts/raw/8a1272454722a54bf3b62764d2ea3ccab54daabb/posts/assets/pg-network.gif" rel="noopener noreferrer"&gt;the application UI results&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that the batch approach makes two requests to the server, while the streaming approach makes only one request. But the &lt;code&gt;Content Download&lt;/code&gt; time is much higher for the streaming approach.&lt;/p&gt;

&lt;p&gt;Could we improve it? Yes, of course!! Let's try to increase the &lt;code&gt;highWaterMark&lt;/code&gt; option of the &lt;code&gt;QueryStream&lt;/code&gt; to 5MB!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slowQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;e6&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;Content Download&lt;/code&gt; time is now ~2 seconds, so by increasing the &lt;code&gt;highWaterMark&lt;/code&gt;, we speed up the download of the data &lt;strong&gt;x2&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Now, if we rerun the 3rd test with the new &lt;code&gt;highWaterMark&lt;/code&gt;, the streaming approach ends in ~10 seconds, so we cut down the time by 1/3!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So the speed relation of the two approaches is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Batching&lt;/strong&gt; : 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming&lt;/strong&gt; : 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;pg-query-stream&lt;/code&gt; library.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, comment, share and follow me on &lt;a href="https://twitter.com/ManuEomm" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>fastify</category>
      <category>postgres</category>
      <category>node</category>
    </item>
    <item>
      <title>The Fastify book is out!</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Fri, 09 Feb 2024 16:06:10 +0000</pubDate>
      <link>https://dev.to/eomm/the-fastify-book-is-out-310o</link>
      <guid>https://dev.to/eomm/the-fastify-book-is-out-310o</guid>
      <description>&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About the Authors:&lt;/strong&gt; 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;amp; CTO of Platformatic, a renowned Open Source author and core maintainer of Fastify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who is this Book For?&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Choose "Accelerating Server-Side Development with Fastify"?&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning Outcomes&lt;/strong&gt; Throughout this book, you'll learn the following essential skills:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Understand encapsulation techniques implemented by Fastify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Become proficient in using Fastify's unique features compared to other frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn to organize project structures and implement architectures for transitioning to microservices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Develop a real-world project to put theory into practice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy, monitor, and handle errors in a running Fastify instance effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Book Structure&lt;/strong&gt; The book is divided into three parts, each addressing different aspects of Fastify development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part One: Fastify Basics&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;What is Fastify?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Plugin System and the Boot Process&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Working with Routes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exploring Hooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exploring Validation and Serialization&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part Two: Building a Real-World Project&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Project Structure and Configuration Management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building a RESTful API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication, Authorization, and Files Handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Application Testing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment and Process Monitoring for a Healthy Application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meaningful Application Logging&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Part Three: Advanced Topics&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;From a Monolith to Microservices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance Assessment and Improvement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developing a GraphQL API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type-Safe Fastify&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're ready to accelerate your server-side development with Fastify, this comprehensive guide is a must-have for your toolkit!&lt;/p&gt;

&lt;p&gt;For more updates and technical discussions, follow the authors on Twitter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Maksim Sinik: @maksimsinik&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manuel Spigolon: @manueomm&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Matteo Collina: @matteocollina&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or subscribe to this blog!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/Accelerating-Server-Side-Development-Fastify-comprehensive/dp/1800563582/"&gt;Get your copy of "Accelerating Server-Side Development with Fastify"&lt;/a&gt; today and embark on a journey towards building scalable and maintainable backend applications!&lt;/p&gt;

</description>
      <category>node</category>
      <category>fastify</category>
      <category>backend</category>
    </item>
    <item>
      <title>Validate the Fastify input with Joi</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Mon, 29 Jan 2024 17:07:47 +0000</pubDate>
      <link>https://dev.to/eomm/validate-the-fastify-input-with-joi-2pd8</link>
      <guid>https://dev.to/eomm/validate-the-fastify-input-with-joi-2pd8</guid>
      <description>&lt;p&gt;If you are an &lt;code&gt;hapi&lt;/code&gt; developer, you may know the &lt;a href="https://www.npmjs.com/package/joi"&gt;&lt;code&gt;joi&lt;/code&gt;&lt;/a&gt; library. It is a powerful validation library that allows you to define programmatic schemas for your data and validate them with ease.&lt;/p&gt;

&lt;p&gt;One of the hardest things about migrating from &lt;code&gt;hapi&lt;/code&gt; to &lt;code&gt;fastify&lt;/code&gt; is the complexity thats involved for you to be able to keep using &lt;code&gt;joi&lt;/code&gt; for the route's input validation.&lt;/p&gt;

&lt;p&gt;But now, thanks to the &lt;a href="https://www.npmjs.com/package/joi-compiler"&gt;&lt;code&gt;joi-compiler&lt;/code&gt;&lt;/a&gt; library, you can use &lt;code&gt;joi&lt;/code&gt; with &lt;code&gt;fastify&lt;/code&gt; without any problem!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; 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 &lt;a href="https://backend.cafe/fastify-v4-book"&gt;Fastify book&lt;/a&gt; too!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;joi-compiler&lt;/code&gt; module replaces the default &lt;code&gt;ajv&lt;/code&gt; schema compiler with a &lt;code&gt;joi&lt;/code&gt;-based one and allows you to use &lt;code&gt;joi&lt;/code&gt; schemas to validate your data. Let's see how to use it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate &lt;code&gt;joi&lt;/code&gt; with &lt;code&gt;fastify&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After you have installed the latest &lt;code&gt;fastify@4&lt;/code&gt; and &lt;code&gt;joi-compiler&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir fastify-joi
cd fastify-joi
npm init -y
npm install --save fastify@4 joi-compiler

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create a simple &lt;code&gt;index.js&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;JoiCompiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joi-compiler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Instantiate the compiler&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JoiCompiler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Install it to Fastify&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schemaController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;compilersFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;buildValidator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buildValidator&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Use it!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joiSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiSchema&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this code, you will be able to test the &lt;code&gt;joi&lt;/code&gt; validation with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X GET http://localhost:3000/
# {"statusCode":400,"error":"Bad Request","message":"\"foo\" is required"}

curl -X GET http://localhost:3000/ -H "foo: bar"
# Hello World!

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;joi&lt;/code&gt; schema is working as expected! In a few lines of code, we have integrated &lt;code&gt;joi&lt;/code&gt; with &lt;code&gt;fastify&lt;/code&gt; and we are able to use it to validate the input of your application routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;joi-compiler&lt;/code&gt; module is a Fastify schema compiler, it is used by Fastify to build the components during the startup to guarantee the &lt;a href="https://backend.cafe/the-complete-guide-to-the-fastify-plugin-system"&gt;encapsulated feature&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this reason, the &lt;code&gt;joi-compiler&lt;/code&gt; can be configured in the &lt;code&gt;schemaController&lt;/code&gt; option during the Fastify application creation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
How the &lt;a href="https://www.fastify.io/docs/latest/Reference/Server/#schemacontroller"&gt;Fastify Schema Controller&lt;/a&gt; works is out of the scope of this article. But if you want to know more about it, you can read the &lt;a href="https://backend.cafe/fastify-v4-book"&gt;Fastify book&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to configure the &lt;code&gt;joi-compiler&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;JoiCompiler&lt;/code&gt; accepts an optional configuration object to customize the &lt;code&gt;joi&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;The default configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JoiCompiler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// optionally: provide all the JOI options you need&lt;/span&gt;
  &lt;span class="c1"&gt;// Here is all the possible options: https://joi.dev/api/?v=17.9.1#anyvalidatevalue-options&lt;/span&gt;
  &lt;span class="na"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stripUnknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// optionally: an array with custom JOI extensions such as `@joi/date`&lt;/span&gt;
  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="c1"&gt;// optionally: if you want to use the async validation. Default: false&lt;/span&gt;
  &lt;span class="na"&gt;asyncValidation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you instantiate the &lt;code&gt;joiCompilerInstance&lt;/code&gt;, the returned object has the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;buildValidator&lt;/code&gt;: The function to pass to the &lt;code&gt;schemaController&lt;/code&gt; option of Fastify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;bucket&lt;/code&gt;: The &lt;code&gt;joi&lt;/code&gt; bucket that contains the schemas when you call the &lt;code&gt;app.addSchema(joiSchema)&lt;/code&gt; method. You can omit this option if you don't use &lt;code&gt;app.addSchema&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;joi&lt;/code&gt;: A customized &lt;code&gt;joi&lt;/code&gt; instance that contains the installed &lt;code&gt;extensions&lt;/code&gt;, if there are any. It is a good practice to use this instance to build your schemas.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use &lt;code&gt;ajv&lt;/code&gt; and &lt;code&gt;joi&lt;/code&gt; together!
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://backend.cafe/fastify-v4-book"&gt;Fastify book&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JoiCompiler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ajv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;toX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;const&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;toY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;const&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pluginJoi&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Install the joi compiler into this encapsulated context!&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setSchemaController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;compilersFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;buildValidator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joiCompilerInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buildValidator&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Let's try to use the external schemas!&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/joi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;toX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;toY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this code, you will be able to use the &lt;code&gt;ajv&lt;/code&gt; and &lt;code&gt;joi&lt;/code&gt; validation in the same application! The &lt;code&gt;ajv&lt;/code&gt; validation will be used for the &lt;code&gt;/ajv&lt;/code&gt; route and the &lt;code&gt;joi&lt;/code&gt; validation will be used for the &lt;code&gt;/joi&lt;/code&gt; route.&lt;/p&gt;

&lt;p&gt;Pretty cool, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.npmjs.com/package/joi-compiler"&gt;&lt;code&gt;joi-compiler&lt;/code&gt;&lt;/a&gt; is a powerful tool that allows you to build and manage &lt;code&gt;joi&lt;/code&gt; instances for Fastify out of the box in a few lines of code. If you're a &lt;code&gt;joi&lt;/code&gt; user, &lt;code&gt;joi-compiler&lt;/code&gt; is definitely worth checking out!&lt;/p&gt;

&lt;p&gt;If you enjoyed this article comment, please share and follow &lt;a href="https://twitter.com/ManuEomm"&gt;@ManuEomm&lt;/a&gt; on Twitter!&lt;/p&gt;

</description>
      <category>fastify</category>
      <category>node</category>
      <category>joi</category>
    </item>
    <item>
      <title>Fastify has a new Application Hook!</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Mon, 18 Sep 2023 06:50:09 +0000</pubDate>
      <link>https://dev.to/eomm/fastify-has-a-new-application-hook-46be</link>
      <guid>https://dev.to/eomm/fastify-has-a-new-application-hook-46be</guid>
      <description>&lt;p&gt;Fastify &lt;a href="https://github.com/fastify/fastify/releases/tag/v4.23.0"&gt;v4.23.0&lt;/a&gt; has just been released, featuring a brand-new &lt;a href="https://github.com/fastify/fastify/pull/4899"&gt;&lt;code&gt;onListen&lt;/code&gt;&lt;/a&gt; Application hook. In this article, we'll explore what it is and how you can leverage it in your Fastify applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Fastify hooks
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Hooks are particularly useful for breaking down your application logic into reusable pieces that can be executed at specific times.&lt;/p&gt;

&lt;p&gt;Fastify offers two primary types of hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application hooks&lt;/strong&gt; : These consist of 5 hooks that are invoked in a specific order during the application's lifecycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request/Reply hooks&lt;/strong&gt; : There are a total of 10 hooks that are called in a specific order during the &lt;a href="https://fastify.dev/docs/latest/Reference/Lifecycle"&gt;HTTP request lifecycle&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each hook type serves a distinct purpose and comes with its own API. Before diving into the new &lt;code&gt;onListen&lt;/code&gt; hook, let's briefly overview Fastify's hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application hooks
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;🛫 The application hooks during application startup include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;onRoute&lt;/code&gt;: Triggered when a route is registered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onRegister&lt;/code&gt;: Invoked when an encapsulated plugin is registered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onReady&lt;/code&gt;: Called when the application is ready but not yet listening to incoming requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;🛬 The application hooks during application shutdown are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;preClose&lt;/code&gt;: Executed before the application is closed, while HTTP requests are still being processed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onClose&lt;/code&gt;: Triggered when the application no longer accepts new requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;preClose&lt;/code&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onClose&lt;/code&gt; 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find more details about these hooks in my &lt;a href="https://backend.cafe/the-fastify-book-is-out"&gt;Fastify book 📙&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  The new &lt;code&gt;onListen&lt;/code&gt; hook
&lt;/h3&gt;

&lt;p&gt;With the release of Fastify &lt;a href="https://github.com/fastify/fastify/releases/tag/v4.23.0"&gt;v4.23.0&lt;/a&gt;, we introduce the sixth application hook: &lt;code&gt;onListen&lt;/code&gt;. This hook is called when the application is ready, and the server is actively listening to incoming requests.&lt;/p&gt;

&lt;p&gt;The API for the &lt;code&gt;onListen&lt;/code&gt; hook is consistent with other application hooks, supporting both async/await and callback styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// async/await style&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onListen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Some async code&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// or callback style&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onListen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Some code&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the application startup sequence, you can now list this new hook as the final step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;onRoute&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onRegister&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onReady&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onListen&lt;/code&gt;: Invoked when the application is ready, and the server is actively handling incoming requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's essential to note that this hook has some differences compared to the others of the same type:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the &lt;code&gt;onListen&lt;/code&gt; hook throws an error, the application will still start anyway, similar to the behavior of the &lt;code&gt;onClose&lt;/code&gt; hook.&lt;/li&gt;
&lt;li&gt;Since the server is already processing HTTP requests, be cautious of potential race conditions if you attempt to load data needed by your routes.&lt;/li&gt;
&lt;li&gt;If you don't call &lt;code&gt;app.listen()&lt;/code&gt; in your tests, this hook will not be executed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, when should you use the &lt;code&gt;onListen&lt;/code&gt; hook?&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The power of the &lt;code&gt;onListen&lt;/code&gt; 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 &lt;code&gt;onReady&lt;/code&gt; hook instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request/Reply hooks
&lt;/h2&gt;

&lt;p&gt;While we won't delve into Request/Reply hooks in this article, you can find comprehensive information in the &lt;a href="https://fastify.dev/docs/latest/Reference/Hooks/#requestreply-hooks"&gt;Fastify documentation&lt;/a&gt; or in chapter 4 of the detailed &lt;a href="https://backend.cafe/the-fastify-book-is-out"&gt;Fastify book 📙&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we've introduced the new &lt;code&gt;onListen&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;If you found this article helpful, please leave a comment, share it with others, and follow me on &lt;a href="https://twitter.com/ManuEomm"&gt;Twitter&lt;/a&gt; for more updates!&lt;/p&gt;

</description>
      <category>node</category>
      <category>fastify</category>
      <category>backend</category>
    </item>
    <item>
      <title>Dynamic GraphQL queries with Mercurius</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Sat, 16 Sep 2023 13:40:25 +0000</pubDate>
      <link>https://dev.to/eomm/dynamic-graphql-queries-with-mercurius-2j06</link>
      <guid>https://dev.to/eomm/dynamic-graphql-queries-with-mercurius-2j06</guid>
      <description>&lt;p&gt;If you're using &lt;a href="https://www.fastify.io" rel="noopener noreferrer"&gt;Fastify&lt;/a&gt; with &lt;a href="https://github.com/mercurius-js/mercurius" rel="noopener noreferrer"&gt;Mercurius&lt;/a&gt; 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.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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, &lt;a href="https://grnh.se/18177b983us" rel="noopener noreferrer"&gt;we are hiring!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What are Dynamic GraphQL queries?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client's query&lt;/li&gt;
&lt;li&gt;The user's role&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's important to note that we are not talking about returning a subset of a generic GraphQL type, but a &lt;strong&gt;completely different GraphQL type&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the schema
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://graphql.org/learn/schema/#union-types" rel="noopener noreferrer"&gt;GraphQL Unions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To begin, let's define our schema using GraphQL Unions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  searchData: Grid
}

union Grid = AdminGrid | ModeratorGrid | UserGrid

type AdminGrid {
  totalRevenue: Float
}

type ModeratorGrid {
  banHammer: Boolean
}

type UserGrid {
  basicColumn: String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, our schema includes a Query type with a &lt;code&gt;searchData&lt;/code&gt; field that returns a &lt;code&gt;Grid&lt;/code&gt; union type. This Grid union type can represent one of three possible types: &lt;code&gt;AdminGrid&lt;/code&gt;, &lt;code&gt;ModeratorGrid&lt;/code&gt;, or &lt;code&gt;UserGrid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, the client can send a query using &lt;a href="https://graphql.org/learn/queries/#inline-fragments" rel="noopener noreferrer"&gt;inline fragments&lt;/a&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  searchData {
    ... on AdminGrid {
      totalRevenue
    }
    ... on ModeratorGrid {
      banHammer
    }
    ... on UserGrid {
      basicColumn
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this is a valid query, &lt;strong&gt;it is not the desired outcome&lt;/strong&gt;. We aim to return a different type based on the user's role, so that the client can send a query like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query {
  searchData {
    totalRevenue
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to note that the above query is not valid against the schema defined above, but with some additional implementation, &lt;strong&gt;we can make it work&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the business logic
&lt;/h2&gt;

&lt;p&gt;Let's start building the basic implementation of our server. You can skip this section if you are already familiar with Fastify and Mercurius.&lt;/p&gt;

&lt;p&gt;The first step is to install the necessary dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;graphql-dynamic-queries
&lt;span class="nb"&gt;cd &lt;/span&gt;graphql-dynamic-queries
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;fastify@4 mercurius@11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, copy the schema we defined above in a &lt;code&gt;gql-schema.js&lt;/code&gt; file, then you need to create an &lt;code&gt;app.js&lt;/code&gt; file where we will write our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GQL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mercurius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./gql-schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// For simplicity, we will start the server only if started with `node app.js run`&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;buildApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildApp&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;searchData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// TODO: implement the business logic&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolveType &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getUserType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buildApp&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserType&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-user-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AdminGrid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moderator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ModeratorGrid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UserGrid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that everything is working, you can start the server running &lt;code&gt;node app.js run&lt;/code&gt; and you should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server listening at http://127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we must list all the use cases we want to implement by adding some test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing our server
&lt;/h3&gt;

&lt;p&gt;We want to test our server with the following use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user with the &lt;code&gt;admin&lt;/code&gt; role should be able to retrieve the &lt;code&gt;totalRevenue&lt;/code&gt; field without inline fragments&lt;/li&gt;
&lt;li&gt;A user with the &lt;code&gt;moderator&lt;/code&gt; role should be able to retrieve the &lt;code&gt;banHammer&lt;/code&gt; field without inline fragments&lt;/li&gt;
&lt;li&gt;A user with the &lt;code&gt;user&lt;/code&gt; role should be able to retrieve the &lt;code&gt;basicColumn&lt;/code&gt; field without inline fragments&lt;/li&gt;
&lt;li&gt;A user without the &lt;code&gt;admin&lt;/code&gt; role should not be able to retrieve the &lt;code&gt;totalRevenue&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;A user without the &lt;code&gt;moderator&lt;/code&gt; role should not be able to retrieve the &lt;code&gt;banHammer&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;A user without the &lt;code&gt;user&lt;/code&gt; role should not be able to retrieve the &lt;code&gt;basicColumn&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;A user without any role should not be able to retrieve any field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We must install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install tap@15 -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can write these tests in a &lt;code&gt;test.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A user with the `admin` role should be able to retrieve the `totalRevenue` field without inline fragments&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;doQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
    query {
      searchData {
        totalRevenue
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;same&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;totalRevenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// TODO: add the other tests&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doQuery&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-user-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userType&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the sake of simplicity, we will not list all the tests here, but you can find the complete complete source code in the &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-dynamic-queries" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running the tests with &lt;code&gt;node test.js&lt;/code&gt; will fail because we have not implemented the business logic yet. So, let's start writing the code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the server-side Dynamic Queries
&lt;/h2&gt;

&lt;p&gt;To implement the business logic, there are these main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve the user's role from the request headers&lt;/li&gt;
&lt;li&gt;Manage the GraphQL query to return the correct type based on the user's role&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's solve the first point.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to retrieve the user's role
&lt;/h3&gt;

&lt;p&gt;We can implement the user role retrieval by installing the &lt;a href="https://github.com/mercurius-js/auth" rel="noopener noreferrer"&gt;&lt;code&gt;mercurius-auth&lt;/code&gt;&lt;/a&gt; plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i mercurius-auth@3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can register the plugin in our &lt;code&gt;app.js&lt;/code&gt; file. To understand what the plugin does, you can read its documentation.&lt;/p&gt;

&lt;p&gt;In the following example, we will compare the &lt;code&gt;x-user-type&lt;/code&gt; HTTP header with the &lt;code&gt;@auth&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Let's start by defining the &lt;code&gt;@auth&lt;/code&gt; directive in the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;directive @auth(
  role: String
) on OBJECT

# ..same as before

type AdminGrid @auth(role: "admin") {
  totalRevenue: Float
}

type ModeratorGrid @auth(role: "moderator") {
  banHammer: Boolean
}

type UserGrid @auth(role: "user") {
  basicColumn: String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can register the plugin in our &lt;code&gt;app.js&lt;/code&gt; file and implement a simple &lt;code&gt;searchData&lt;/code&gt; resolver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildApp&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// the same as before&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;searchData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getUserType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AdminGrid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;totalRevenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ModeratorGrid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;banHammer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;basicColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;basic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// the same as before &lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mercurius-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;authContext &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-user-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;applyPolicy &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Applying policy %s on user %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;// we compare the schema role directive with the user role&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;authDirective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the user should be able to retrieve the &lt;code&gt;totalRevenue&lt;/code&gt; field only if the &lt;code&gt;x-user-type&lt;/code&gt; header is set to &lt;code&gt;admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nevertheless, we can't run the tests yet because we have not implemented the second point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the Dynamic Queries
&lt;/h3&gt;

&lt;p&gt;The last step is to implement the dynamic queries. Right now, our tests are failing with the following error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Cannot query field 'totalRevenue' on type 'Grid'. Did you mean to use an inline fragment on 'AdminGrid'?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The error is correct because we are not using inline fragments to query a Union type.&lt;/p&gt;

&lt;p&gt;To overcome this issue, we can use the mercurius &lt;a href="https://github.com/mercurius-js/mercurius/blob/master/docs/hooks.md#prevalidation" rel="noopener noreferrer"&gt;&lt;code&gt;preValidation&lt;/code&gt;&lt;/a&gt; hook. Let's try to see how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNptkMFuwjAMhl_F8qlI8AI9TIIG7bIdNnYjHKLE0AqSFCeZQJR3x213nE-W_8_-bT_QRkdY44lN38KP0gEk1vvm0lHIB1it3ob3rw-4FuL7AJv9jviX-DBzm1GHpuo5WkoJRlJFW7z0LmakmRBV2ZbsGUzJLTiTzZ-qJnVb0Y1syfTfgO2EDN-U-hgSDWtcoif2pnOy92OENOaWPGmsJXWGzxp1eAondnF3DxbrzIWWWHrxJtUZOddjfTSXJFVyXY78OT9i-sfzBTsWWD4" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fpako%3AeNptkMFuwjAMhl_F8qlI8AI9TIIG7bIdNnYjHKLE0AqSFCeZQJR3x213nE-W_8_-bT_QRkdY44lN38KP0gEk1vvm0lHIB1it3ob3rw-4FuL7AJv9jviX-DBzm1GHpuo5WkoJRlJFW7z0LmakmRBV2ZbsGUzJLTiTzZ-qJnVb0Y1syfTfgO2EDN-U-hgSDWtcoif2pnOy92OENOaWPGmsJXWGzxp1eAondnF3DxbrzIWWWHrxJtUZOddjfTSXJFVyXY78OT9i-sfzBTsWWD4%3Ftype%3Dpng"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a client sends a GraphQL query, the server will process the GQL Document in the &lt;code&gt;preValidation&lt;/code&gt; hook, before validating the GQL against the GQL Schema.&lt;/p&gt;

&lt;p&gt;In this hook, &lt;strong&gt;we can modify the GQL Document&lt;/strong&gt; sent by the user to add the inline fragments. So, after the &lt;code&gt;mercurius-auth&lt;/code&gt; plugin registration, we can add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildApp&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// the same as before&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the same as before &lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mercurius-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the same as before&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preValidation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;visit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// We check if we must add the inline fragment&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isMaskedQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                              &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                              &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                              &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMaskedQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// This is the magic, we add a new inline fragment modifying the AST&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodeWithInlineFragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SelectionSet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;InlineFragment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;typeCondition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NamedType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userType&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                  &lt;span class="na"&gt;selectionSet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectionSet&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodeWithInlineFragment&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// We apply the new AST to the GQL Document&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;definitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;definitions&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous code, we inspect the GraphQL &lt;a href="(https://backend.cafe/what-is-ast)"&gt;Document Abstract Syntax Tree (AST)&lt;/a&gt; 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.&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the GraphQL Document against the GraphQL Schema&lt;/li&gt;
&lt;li&gt;Apply the authentication policy&lt;/li&gt;
&lt;li&gt;Resolve the query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this implementation, when running tests, everything should pass successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoiding breaking changes on the client side to support new features&lt;/li&gt;
&lt;li&gt;Hiding GraphQL Types from the client through disabling schema introspection&lt;/li&gt;
&lt;li&gt;Applying query optimizations for the client&lt;/li&gt;
&lt;li&gt;Providing a specialized response based on the user type instead of a generic one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is just a small example of the possibilities when using Mercurius and manipulating the GraphQL Document.&lt;/p&gt;

&lt;p&gt;If you have found this helpful, you may read &lt;a href="https://backend.cafe/series/mercurius" rel="noopener noreferrer"&gt;other articles about Mercurius&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now jump into the &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-dynamic-queries" rel="noopener noreferrer"&gt;source code on GitHub&lt;/a&gt; and start to play with the GraphQL implemented in Fastify.&lt;/p&gt;

&lt;p&gt;If you enjoyed this article comment, share and follow me on &lt;a href="https://twitter.com/ManuEomm" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>backend</category>
      <category>node</category>
    </item>
    <item>
      <title>How to use DataLoader with Mercurius GraphQL</title>
      <dc:creator>Manuel Spigolon</dc:creator>
      <pubDate>Sat, 05 Aug 2023 09:29:06 +0000</pubDate>
      <link>https://dev.to/eomm/how-to-use-dataloader-with-mercurius-graphql-24fn</link>
      <guid>https://dev.to/eomm/how-to-use-dataloader-with-mercurius-graphql-24fn</guid>
      <description>&lt;h2&gt;
  
  
  Solve the N+1 Problem Using DataLoader with Mercurius GraphQL
&lt;/h2&gt;

&lt;p&gt;by &lt;em&gt;&lt;a href="https://twitter.com/ManuEomm"&gt;Manuel Spigolon&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are using &lt;a href="https://www.fastify.io"&gt;Fastify&lt;/a&gt; with &lt;a href="https://mercurius.dev/#/"&gt;Mercurius&lt;/a&gt; as the GraphQL adapter, you are probably looking for a solution to the N+1 problem. This article will show you how to solve it and speed up your GraphQL application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not using Fastify instead, you can read a &lt;a href="https://backend.cafe/how-to-log-useful-data-from-a-graphql-request"&gt;Quick Start guide&lt;/a&gt;before reading this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is the N+1 problem?
&lt;/h3&gt;

&lt;p&gt;I must say that I could not find a TL;DR (Too Long; Didn't Read) explanation of the N+1 problem to suggest for you to read before continuing. So, I will try to explain it with a quick code example that we will fix later in this article.&lt;/p&gt;

&lt;p&gt;Let's see the N+1 problem &lt;strong&gt;in action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First of all, we need an application up &amp;amp; running. Create a &lt;code&gt;gql-schema.js&lt;/code&gt; file that will contain a simple GQL Schema string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  developers: [Developer]
}

type Developer {
  id: Int
  name: String
  builtProjects: [Project]
}

type Project {
  id: Int
  name: String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's connect the previous schema to a new &lt;code&gt;app.js&lt;/code&gt; file, where we will implement a Fastify + Mercurius application.&lt;em&gt;We will use an in-memory database to store the mock data. You can find the SQL data used for this article in the &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-loader"&gt;source code on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mercurius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mercurius&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gqlSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./gql-schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize an in-memory SQLite database&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify-sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;promiseApi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// For the sake of the test, we are going to create a table with some data&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;migrationsPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;migrations/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// This is the resolver for the Query.developers field&lt;/span&gt;
      &lt;span class="na"&gt;developers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM Developers`&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql: %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the resolver for the Developer Typo Object&lt;/span&gt;
    &lt;span class="na"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;builtProjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM Projects WHERE devId = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql: %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mercurius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gqlSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;graphiql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3001&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, we are ready to start our application by running the &lt;code&gt;node app.js&lt;/code&gt; command. Thanks to the &lt;code&gt;graphiql: true&lt;/code&gt; option, we can open the GraphiQL interface at &lt;code&gt;http://localhost:3001/graphiql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From the GraphiQL interface, we can run the following query by hitting the &lt;code&gt;Play&lt;/code&gt; button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  developers {
    name
    builtProjects {
      name
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good! You should see the server's output on the right side of the GraphiQL interface. However, if we look at the server's logs, we can see that the server has executed 4 SQL queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Eomm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sql: SELECT * FROM Developers"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Eomm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sql: SELECT * FROM Projects WHERE devId = 1"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Eomm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sql: SELECT * FROM Projects WHERE devId = 2"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Eomm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sql: SELECT * FROM Projects WHERE devId = 3"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the queries are not optimized because we ran a query to fetch the projects for each developer instead of fetching all the projects in a single query.&lt;/p&gt;

&lt;p&gt;Now you have seen the N+1 problem in action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; : we run a root query to fetch the first data list&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;+N&lt;/strong&gt; : we run a query for each item of the previous list to fetch the related data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, if we had 100 developers, we would run 101 queries instead of 2! Now that we have seen the problem, let's solve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to solve the N+1 problem?
&lt;/h3&gt;

&lt;p&gt;The most common way to solve the N+1 problem is to use &lt;strong&gt;DataLoaders&lt;/strong&gt;. The DataLoader allows you to batch and cache the results of your queries and reuse them when necessary.&lt;/p&gt;

&lt;p&gt;Mercurius offers you two ways to use DataLoader:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mercurius-js/mercurius/blob/HEAD/docs/loaders.md"&gt;&lt;strong&gt;Loader&lt;/strong&gt;&lt;/a&gt;: it is a built-in DataLoader-Like solution that is quick to set up and use.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/graphql/dataloader"&gt;&lt;strong&gt;DataLoader&lt;/strong&gt;&lt;/a&gt;: it is the standard solution to N+1 problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we are going to see both solutions and compare them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mercurius Loader in action
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;loader&lt;/code&gt; feature is a built-in DataLoader-Like solution that is quick to set up and use. It &lt;strong&gt;replaces&lt;/strong&gt; Mercurius' &lt;code&gt;resolvers&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Let's see how to use it by optimizing the previous &lt;code&gt;app.js&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... previous code&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;builtProjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;devIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM Projects WHERE devId IN (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;devIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql: %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... previous code&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Delete the `builtProjects` resolver. It would be ignored in any case&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... previous code&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mercurius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gqlSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;graphiql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// add the loaders option&lt;/span&gt;
  &lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we have replaced the &lt;code&gt;resolvers.Developer.builtProjects&lt;/code&gt; function with the &lt;code&gt;loaders&lt;/code&gt; one. The difference is that the &lt;code&gt;loaders&lt;/code&gt; receive an array of queries (results from the parent query) instead of a single &lt;code&gt;parent&lt;/code&gt; object. Mercurius will batch the queries and call the &lt;code&gt;loader&lt;/code&gt; function only once.&lt;/p&gt;

&lt;p&gt;In this new loader function, you can run a single query to fetch all the data you need and then you must return a positionally-matched array of results.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is quick to set up and use.&lt;/li&gt;
&lt;li&gt;It is not necessary to pollute the context.&lt;/li&gt;
&lt;li&gt;It is managed by Mercurius.&lt;/li&gt;
&lt;li&gt;Clear separation of concerns between the resolvers and the loaders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is not possible to reuse the loader's cache in other resolvers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  DataLoader in action
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;DataLoader&lt;/code&gt; is the standard solution to the N+1 problem. It was originally created by Facebook. Let's see how we can integrate it into our application.&lt;/p&gt;

&lt;p&gt;First, you should restore the &lt;code&gt;app.js&lt;/code&gt; file removing the &lt;code&gt;loaders&lt;/code&gt; configuration. Second, we need to install the &lt;code&gt;dataloader&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install dataloader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we must instantiate a DataLoader for each request, so we need to extend the &lt;code&gt;context&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DataLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dataloader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// ... previous code&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... previous code&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Developer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;builtProjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectsDataLoader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mercurius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gqlSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;graphiql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Instantiate a DataLoader for each request&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectsDataLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DataLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`SELECT * FROM Projects WHERE devId IN (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sql: %s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sqlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// decorate the context with the dataloader&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;projectsDataLoader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this new example, we have added the new &lt;code&gt;projectsDataLoader&lt;/code&gt; object to the Mercurius &lt;code&gt;context&lt;/code&gt;. This object is an instance of the DataLoader class that we have imported from the &lt;code&gt;dataloader&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;DataLoader&lt;/code&gt; class accepts a &lt;code&gt;batchLoader&lt;/code&gt; function that will be called only once for each batch of queries. It supports different ways to accumulate the queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frame of execution: it is the default behavior. It accumulates the queries until the next tick. It is the same approach used by Mercurius Loader.&lt;/li&gt;
&lt;li&gt;Time frame: it accumulates the queries until the specified time frame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;batchLoader&lt;/code&gt; function receives an array of keys as a single argument, and it must return an array of results positionally matching the input array. As you can see, it is the same approach used by Mercurius Loader.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a standard defacto solution.&lt;/li&gt;
&lt;li&gt;Flexibility: it is possible to reuse the loader cache in other resolvers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires more code to set up and configure, you need to create your own &lt;code&gt;context&lt;/code&gt; to access the database.&lt;/li&gt;
&lt;li&gt;The resolvers must be aware and use the &lt;code&gt;DataLoader&lt;/code&gt; instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;You have now learned how to use DataLoaders with Mercurius by exploring two different solutions to solve the N+1 problem. You may think that mixing the resolvers and the loaders could be a good idea. Surely it is doable, but you must turn off one of the two caches to avoid inconsistencies and it could be a bit confusing to manage.&lt;/p&gt;

&lt;p&gt;If you have found this helpful, you may read &lt;a href="https://backend.cafe/series/mercurius"&gt;other articles about Mercurius&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now jump into the &lt;a href="https://github.com/Eomm/fastify-discord-bot-demo/tree/HEAD/bonus/graphql-loader"&gt;source code on GitHub&lt;/a&gt; and start to play with the GraphQL implemented in Fastify.&lt;/p&gt;

&lt;p&gt;If you enjoyed this article comment, share and follow me on &lt;a href="https://twitter.com/ManuEomm"&gt;twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>node</category>
      <category>fastify</category>
      <category>mercurius</category>
      <category>graphql</category>
    </item>
  </channel>
</rss>
