<?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: Callis Ezenwaka</title>
    <description>The latest articles on DEV Community by Callis Ezenwaka (@callezenwaka).</description>
    <link>https://dev.to/callezenwaka</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%2F1048990%2F4960428d-2f69-433f-be2b-dc70dd46664c.jpeg</url>
      <title>DEV Community: Callis Ezenwaka</title>
      <link>https://dev.to/callezenwaka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/callezenwaka"/>
    <language>en</language>
    <item>
      <title>Building a Multi-Service Application with a Shared Database Layer.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Tue, 25 Mar 2025 09:55:25 +0000</pubDate>
      <link>https://dev.to/callezenwaka/building-a-multi-service-application-with-a-shared-database-layer-101c</link>
      <guid>https://dev.to/callezenwaka/building-a-multi-service-application-with-a-shared-database-layer-101c</guid>
      <description>&lt;p&gt;In today's post, I'll walk through how to build a multi-service application that shares a common database layer. This architecture pattern is particularly useful when you're breaking a monolith into microservices or when you want to separate concerns while maintaining data consistency. All code examples and the complete implementation are available on GitHub at &lt;a href="https://github.com/callezenwaka/database" rel="noopener noreferrer"&gt;https://github.com/callezenwaka/database&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;Let's look at the architecture we'll be building:&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;.&lt;/span&gt;
├── LICENSE
├── README.md
├── backend
│   ├── Dockerfile
│   ├── README.md
│   ├── src
│   └── tsconfig.json
├── database
│   ├── Dockerfile
│   ├── README.md
│   ├── src
│   └── tsconfig.json
├── docker-compose.yml
└── server
    ├── Dockerfile
    ├── README.md
    ├── src
    └── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure follows a clean separation of concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database: Contains shared database functionality&lt;/li&gt;
&lt;li&gt;backend: Application backend service&lt;/li&gt;
&lt;li&gt;server: Application server service&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Shared Database Package
&lt;/h2&gt;

&lt;p&gt;The core of our architecture is the shared database package which provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A unified connection mechanism&lt;/li&gt;
&lt;li&gt;Migration management&lt;/li&gt;
&lt;li&gt;Consistent APIs for database operations&lt;/li&gt;
&lt;li&gt;Entity definitions shared across services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Database Package Overview
&lt;/h2&gt;

&lt;p&gt;The database package is designed to be consumed by other services within our application. It's not published to npm but referenced locally by other packages.&lt;br&gt;
Here's what makes it special:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically builds TypeScript code when installed by other services&lt;/li&gt;
&lt;li&gt;Provides a consistent interface for database operations&lt;/li&gt;
&lt;li&gt;Handles connection pooling and lifecycle management&lt;/li&gt;
&lt;li&gt;Centralizes migration and seeding logic&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Implementation Details
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Setting Up the Database Package
&lt;/h3&gt;

&lt;p&gt;The database package uses TypeORM to interface with PostgreSQL. Here's how to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;getDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeDatabase&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;@database/database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Connect to the database&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;connectToDatabase&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;dataSource&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;getDataSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Use the dataSource for database operations&lt;/span&gt;

  &lt;span class="c1"&gt;// When done, close the connection&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;closeDatabase&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;h3&gt;
  
  
  Creating a Resilient Service
&lt;/h3&gt;

&lt;p&gt;Let's examine a real-world implementation from our backend service's entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// backend/src/index.ts&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// other codes&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;bootstrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Start the server first&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="o"&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="nx"&gt;PORT&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;logger&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="s2"&gt;`Backend running on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&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="c1"&gt;// Check Redis connection&lt;/span&gt;
    &lt;span class="k"&gt;try&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;pingResult&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;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pingResult&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PONG&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;logger&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;Redis connection established:&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="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisError&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Redis connection failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;redisError&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;redisError&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisError&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="c1"&gt;// Redis will automatically attempt reconnection based on its config&lt;/span&gt;
      &lt;span class="nx"&gt;redisClient&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;connect&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt; &lt;span class="o"&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;logger&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;Redis connection restored&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;redisClient&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;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="o"&gt;=&amp;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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt; &lt;span class="o"&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Then attempt database connection&lt;/span&gt;
    &lt;span class="k"&gt;try&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;getDataSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;logger&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;Database connection established. Full functionality available.&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="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databaseAvailable&lt;/span&gt; &lt;span class="o"&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logger&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbError&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;databaseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dbError&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;dbError&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="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Database connection failed. Operating in limited functionality mode:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;databaseError&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databaseAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Attempt periodic reconnection with attempt tracking&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;reconnectAttempts&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_RECONNECT_ATTEMPTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set to -1 for unlimited attempts&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reconnectInterval&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="nx"&gt;reconnectAttempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;try&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;getDataSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;logger&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="s2"&gt;`Database connection established after &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reconnectAttempts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; attempts. Full functionality restored.`&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databaseAvailable&lt;/span&gt; &lt;span class="o"&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="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reconnectAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reconnectAttempts&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;reconnectInterval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Log with attempt count&lt;/span&gt;
          &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Database reconnection attempt &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reconnectAttempts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="c1"&gt;// Check if we've reached the max attempts (if not unlimited)&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;MAX_RECONNECT_ATTEMPTS&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;reconnectAttempts&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_RECONNECT_ATTEMPTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;logger&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="s2"&gt;`Maximum reconnection attempts (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;MAX_RECONNECT_ATTEMPTS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) reached. Giving up on database connection.`&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbReconnectionExhausted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;reconnectInterval&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="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// try every 30 seconds&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle graceful shutdown&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shutdown&lt;/span&gt; &lt;span class="o"&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;logger&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;Shutting down server...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Shutdown logic for server, Redis, and database&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle termination signals&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;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shutdown&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="nx"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to start server:&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="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&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;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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;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="mi"&gt;1&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="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the Database Package in Services
&lt;/h3&gt;

&lt;p&gt;To use the database package in your services, add it as a local dependency:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&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="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@database/database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file:../database"&lt;/span&gt;&lt;span class="w"&gt;
  &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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a symbolic link to the database package, allowing your service to import its functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Starting the Development Environment
&lt;/h3&gt;

&lt;p&gt;Our project includes Docker configurations for easy local development:&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;# Start docker components&lt;/span&gt;
docker-compose down &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Confirm database is running&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; database-app-postgres-1 psql &lt;span class="nt"&gt;-U&lt;/span&gt; app_user &lt;span class="nt"&gt;-d&lt;/span&gt; app_db

&lt;span class="c"&gt;# Check service logs&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 20 database-service-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running Services
&lt;/h3&gt;

&lt;p&gt;If you decide to run the servers locally, you could use the command:&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;# Start server&lt;/span&gt;
npm run dev &lt;span class="c"&gt;# npm run start:dev - for initialization&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Running Migrations
&lt;/h3&gt;

&lt;p&gt;The database package includes scripts for managing database migrations, which are defined in the package.json file:&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@database/database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"migrate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node src/run-migrations.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typeorm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typeorm-ts-node-commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"migration:run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typeorm-ts-node-commonjs migration:run -d src/index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"migration:generate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typeorm-ts-node-commonjs migration:generate -d src/index.ts -n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"migration:create"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typeorm-ts-node-commonjs migration:create src/migrations/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"seed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node src/seeds/run-seeds.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run migration:run &amp;amp;&amp;amp; npm run seed"&lt;/span&gt;&lt;span class="w"&gt;
  &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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These scripts make database management convenient:&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;# Run all pending migrations&lt;/span&gt;
npm run migration:run

&lt;span class="c"&gt;# Generate a new migration based on entity changes&lt;/span&gt;
npm run migration:generate &lt;span class="nt"&gt;--&lt;/span&gt; MigrationName

&lt;span class="c"&gt;# Create a new empty migration&lt;/span&gt;
npm run migration:create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Seeding Data
&lt;/h3&gt;

&lt;p&gt;For development and testing, you can seed your database using the predefined scripts:&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;# Run seed scripts&lt;/span&gt;
npm run seed

&lt;span class="c"&gt;# Reset database (run migrations and seeds)&lt;/span&gt;
npm run reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reset command is particularly useful for local development as it runs all migrations and then seeds the database with test data in one step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resilient Connection Management
&lt;/h2&gt;

&lt;p&gt;A robust multi-service application needs to handle connection issues gracefully. Let's break down the key components of our resilient connection management:&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection States
&lt;/h3&gt;

&lt;p&gt;Our backend service tracks connection states in the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databaseAvailable&lt;/span&gt; &lt;span class="o"&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;// Database connection status&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;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisAvailable&lt;/span&gt; &lt;span class="o"&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;// Redis connection status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the application to provide limited functionality even when connections fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Reconnection
&lt;/h3&gt;

&lt;p&gt;For database connections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The service attempts reconnection with an exponential backoff strategy&lt;/li&gt;
&lt;li&gt;Reconnection attempts are tracked and can be limited&lt;/li&gt;
&lt;li&gt;The application maintains state awareness throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Redis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We leverage Redis's built-in reconnection capabilities&lt;/li&gt;
&lt;li&gt;Event listeners track connection state changes&lt;/li&gt;
&lt;li&gt;The application adapts functionality based on availability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Graceful Shutdown
&lt;/h3&gt;

&lt;p&gt;The shutdown process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stops accepting new requests&lt;/li&gt;
&lt;li&gt;Closes the HTTP server&lt;/li&gt;
&lt;li&gt;Terminates Redis connections&lt;/li&gt;
&lt;li&gt;Closes database connections&lt;/li&gt;
&lt;li&gt;Exits the process with appropriate status code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures all resources are properly released when the application terminates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Benefits
&lt;/h2&gt;

&lt;p&gt;This architecture offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code reuse: Database logic is written once and shared across services&lt;/li&gt;
&lt;li&gt;Consistency: All services interact with the database in the same way&lt;/li&gt;
&lt;li&gt;Maintenance: Database changes affect a single package, not multiple services&lt;/li&gt;
&lt;li&gt;Separation of concerns: Each service focuses on its core functionality&lt;/li&gt;
&lt;li&gt;Resilience: Services can handle connection failures gracefully&lt;/li&gt;
&lt;li&gt;Self-healing: Automatic reconnection attempts restore full functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;We can test the full implementation by spinning up &lt;code&gt;docker compose&lt;/code&gt; with docker command &lt;code&gt;docker-compose down -v &amp;amp;&amp;amp; docker-compose up -d&lt;/code&gt;.&lt;br&gt;
Then, we could use postman to make API calls. To add a new user, make a POST request to the backend POST endpoint.&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="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User Test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user@test.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="w"&gt;
&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;While to add a new blog, make a POST API call to server endpoint:&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="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Getting Started with Coding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A new beginner's guide to TypeScript and its powerful features."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User Test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&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;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you encounter issues importing from the database package, ensure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The package has been built (&lt;code&gt;dist&lt;/code&gt; directory exists)&lt;/li&gt;
&lt;li&gt;The service has the correct reference in package.json&lt;/li&gt;
&lt;li&gt;TypeScript paths are configured correctly in the consuming service&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;A shared database layer can simplify development across multiple services while maintaining consistency and reducing duplication. By centralizing database logic, you create a more maintainable system that's easier to evolve over time.&lt;/p&gt;

&lt;p&gt;If you would like to explore this architecture further or use it as a starting point for your own projects, check out the complete implementation at &lt;a href="https://github.com/callezenwaka/database" rel="noopener noreferrer"&gt;https://github.com/callezenwaka/database&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The repository includes all the code we have discussed in this post, plus additional examples and configurations that can help you get started quickly.&lt;/p&gt;

&lt;p&gt;Have you implemented a similar architecture? I'd love to hear about your experiences in the comments!&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>node</category>
      <category>typescript</category>
      <category>docker</category>
    </item>
    <item>
      <title>Using Edge Functions to Mitigate Cost and Latency for Distributed Users.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Wed, 26 Apr 2023 15:43:16 +0000</pubDate>
      <link>https://dev.to/callezenwaka/using-edge-function-infrastructures-to-mitigate-growing-demand-while-minimizing-cost-and-latency-for-distributed-users-2l2p</link>
      <guid>https://dev.to/callezenwaka/using-edge-function-infrastructures-to-mitigate-growing-demand-while-minimizing-cost-and-latency-for-distributed-users-2l2p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Edge functions is a piece of code that executes on an “edge network” or a “Content Delivery Network” (CDN). Edge Networks, which consist of servers that sit on the "edge" of multiple networks, act as bridges to route traffic between nodes or networks across the CDN (1).&lt;/p&gt;

&lt;p&gt;Edge networks are networks of servers, distributed around the world, working together to serve cached content to users from the closest server location possible. Edge functions are serverless functions while all serverless functions are not edge functions.&lt;/p&gt;

&lt;p&gt;Serverless functions are codes deployed mostly on the cloud and executed on demand, without need for provisioning their own servers, or scaling their application to meet traffic spikes. They are also available in broad geographic regions.&lt;/p&gt;

&lt;p&gt;In contrast, Edge functions are serverless functions executed in the location closest to the user’s request. Edge functions are not constrained to specific pieces of code or function. They have the capacity to run just any other business applications.&lt;/p&gt;

&lt;p&gt;Since all the business logic is deployed to an edge function, when the user sends a request to a node, it is the function that determines how to respond to the browser. The following cloud providers offer edge functions or a variant of it: Netlify, Vercel, AWS, Azion, Cloudflare, Azure etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Edge Functions
&lt;/h2&gt;

&lt;p&gt;Edge functions deliver content to users with a fast and personalized experience, and has the following additional benefits (2):&lt;br&gt;
• Decreased latency.&lt;br&gt;
• Reduction in cold start boot times.&lt;br&gt;
• Conditional routing.&lt;br&gt;
• Adding a custom HTTP response header.&lt;br&gt;
• A/B testing.&lt;br&gt;
• Setting a cookie.&lt;br&gt;
• Personalizing by geolocation.&lt;br&gt;
• Authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Drawbacks
&lt;/h2&gt;

&lt;p&gt;However, while edge functions offer users the above benefits, they nevertheless have the following drawbacks:&lt;br&gt;
• Latency due to data source location or origin.&lt;br&gt;
• Low execution time.&lt;br&gt;
• Limited memory and functions size.&lt;br&gt;
• Capped invocation limits.&lt;br&gt;
• Unavailable and unsupported native APIs.&lt;br&gt;
• Maximum initial response time.&lt;/p&gt;

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

&lt;p&gt;With the exception of drawbacks, some of which were listed above, edge functions have the potential to reduce cost as it will reduce the latency between a user request and the response time. &lt;/p&gt;

&lt;p&gt;They have reduced cold start boot time and thus, could intercept user requests faster and deliver personalized responses based on the user location. Such use of geolocation for content delivery and data management could help mitigate compliance or regulatory challenges such as GDPR. &lt;/p&gt;

&lt;p&gt;Of the different cloud providers researched, Amazon CloudFront appears to be a more dominant player in this field. Not only that it has two different flavors for edge networks, but it also has the capability to serve video on demand or live stream real-time events, encrypt specific fields throughout system processing, and serve private content using edge customizations (3).&lt;/p&gt;

&lt;p&gt;Since supporting real-time communication between distributed users is an operation that has value only during the interaction time. Hence, round tripping to the main data center could introduce latency and its associated cost. &lt;/p&gt;

&lt;p&gt;Edge networks have far reached advantages, most of which were outlined above. The use of edge networks could suffice and deliver more secure, reliable, effective, and affordable services to distributed users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://whitep4nth3r.com/blog/what-is-the-edge-serverless-functions/#edge-functions-are-serverless-functions-at-the-edge"&gt;https://whitep4nth3r.com/blog/what-is-the-edge-serverless-functions/#edge-functions-are-serverless-functions-at-the-edge&lt;/a&gt;&lt;br&gt;
&lt;a href="https://vercel.com/docs/concepts/functions/edge-functions/limitations"&gt;https://vercel.com/docs/concepts/functions/edge-functions/limitations&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/IntroductionUseCases.html"&gt;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/IntroductionUseCases.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.tigera.io/blog/containerized-air-gapped-edge-platform-architecture/"&gt;https://www.tigera.io/blog/containerized-air-gapped-edge-platform-architecture/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.netlify.com/blog/edge-functions-explained/"&gt;https://www.netlify.com/blog/edge-functions-explained/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.azion.com/en/documentation/products/edge-application/edge-functions/"&gt;https://www.azion.com/en/documentation/products/edge-application/edge-functions/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developers.cloudflare.com/workers/learning/using-websockets/"&gt;https://developers.cloudflare.com/workers/learning/using-websockets/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://azure.microsoft.com/en-us/blog/microsoft-partners-with-the-industry-to-unlock-new-5g-scenarios-with-azure-edge-zones/"&gt;https://azure.microsoft.com/en-us/blog/microsoft-partners-with-the-industry-to-unlock-new-5g-scenarios-with-azure-edge-zones/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloudcomputing</category>
      <category>webdev</category>
      <category>serverless</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Real-Time Data Visualization with D3.js and Vue.js.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Sat, 22 Apr 2023 22:48:24 +0000</pubDate>
      <link>https://dev.to/callezenwaka/real-time-data-visualization-with-d3js-and-vuejs-4og3</link>
      <guid>https://dev.to/callezenwaka/real-time-data-visualization-with-d3js-and-vuejs-4og3</guid>
      <description>&lt;p&gt;Many web applications and solutions are becoming more dynamic and responsive. This is, in part, due to high and scalable computing capacity. More web applications are deployed on various cloud platforms as a result of high reliability, availability and security.&lt;/p&gt;

&lt;p&gt;To add to this, is the end users' continual demand for real time experience. As organizations becomes more data-driven, many are using data visualizations to either tell their stories or share real time feedback with their customers.&lt;/p&gt;

&lt;p&gt;This tutorial will use &lt;a href="https://d3js.org/"&gt;D3.js&lt;/a&gt;, a JavaScript library for manipulating documents based on data and &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;, a progressive JavaScript framework used for building user interfaces, to demonstrate real time visualization.&lt;/p&gt;

&lt;p&gt;Firstly, we will create a vue.js project using &lt;a href="https://vitejs.dev/"&gt;vite.js&lt;/a&gt;, a build tool for modern web development that is designed to be fast and efficient. If you do not have vite already installed, run the following command &lt;code&gt;npm create vite@latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, navigate to project directory and run &lt;code&gt;npm init vue@latest&lt;/code&gt; and follow the prompt by picking a project name, selecting TypeScript and other relevant features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Project name: … &amp;lt;your-project-name&amp;gt;
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

Scaffolding project in ./&amp;lt;your-project-name&amp;gt;...
Done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will scaffold a vue.js project with similar project tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── README.md
├── env.d.ts
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── favicon.ico
├── src
│   ├── App.vue
│   ├── assets
│   ├── components
│   ├── main.ts
│   ├── router
│   ├── types
│   └── views
├── tsconfig.app.json
├── tsconfig.config.json
├── tsconfig.json
├── tsconfig.vitest.json
└── vite.config.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, confirm that your &lt;code&gt;package.json&lt;/code&gt; file has the following dependencies, else copy and paste (more reliably install them individually if the latest version is your thing) them. Make sure that you are inside your project directory. Otherwise, change to your project directory and run &lt;code&gt;npm install&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "dependencies": {
    "@types/d3": "^7.4.0",
    "@types/d3-shape": "^3.1.0",
    "d3": "^7.6.1",
    "d3-shape": "^3.1.0",
    "vue": "^3.2.45",
    "vue-router": "^4.1.6",
    "vuex": "^4.1.0"
  },
  "devDependencies": {
    "@rushstack/eslint-patch": "^1.1.4",
    "@types/jsdom": "^20.0.1",
    "@types/node": "^18.11.12",
    "@vitejs/plugin-vue": "^4.0.0",
    "@vue/eslint-config-prettier": "^7.0.0",
    "@vue/eslint-config-typescript": "^11.0.0",
    "@vue/test-utils": "^2.2.6",
    "@vue/tsconfig": "^0.1.3",
    "eslint": "^8.22.0",
    "eslint-plugin-vue": "^9.3.0",
    "jsdom": "^20.0.3",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.7.1",
    "typescript": "~4.7.4",
    "vite": "^4.0.0",
    "vitest": "^0.25.6",
    "vue-tsc": "^1.0.12"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the components directory and create the chart folder (Barchart for this example) and a BarChart.vue file as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── BarChart
│   └── BarChart.vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;.vue&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class="" ref="barChartRef"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a vue.js code template with a &lt;code&gt;ref?: VNodeRef&lt;/code&gt; property called &lt;code&gt;barChartRef&lt;/code&gt;. We will define the &lt;code&gt;ref&lt;/code&gt; inside the script section. Next create a script section and import the D3.js library and other Vue.js dependencies. &lt;/p&gt;

&lt;p&gt;For demonstration purposes, we will be skipping some codes here but are available in the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script setup lang="ts"&amp;gt;
// @ is an alias to /src
import * as d3 from "d3";
import { onMounted, onBeforeUnmount, computed, ref, reactive, watch, } from "vue";
...

&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also define our data, data interface (schema) and other variables for the chart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Input {
  label: string;
  value: number;
}

const barChartRef = ref&amp;lt;HTMLElement | null&amp;gt;(null);
const margin = reactive({ top: 30, right: 20, bottom: 30, left: 40 });
const height = ref(360);
const width = ref(
  (barChartRef?.value?.offsetWidth || 300) - margin.left - margin.right
);

const revenue = ref([
  {
    label: "2013-06-30",
    value: 660,
  },
  {
    label: "2014-12-31",
    value: 814,
  },
  {
    label: "2015-06-30",
    value: 1131,
  },
  {
    label: "2016-12-31",
    value: 1267,
  },
  {
    label: "2017-06-30",
    value: 1514,
  },
  {
    label: "2018-12-31",
    value: 1763,
  },
  {
    label: "2019-06-30",
    value: 2653,
  },
  {
    label: "2020-12-31",
    value: 6148,
  },
  {
    label: "2021-06-30",
    value: 12394,
  },
  {
    label: "2022-12-31",
    value: 2162,
  },
]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we will transform our data to the right format as below using a computed function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const data = computed(() =&amp;gt; {
  return revenue.value.map((d: Input) =&amp;gt; {
    return {
      label: d.label,
      value: d.value,
    };
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to use other Vue.js native API functions to control how our chart is rendered on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onMounted(async () =&amp;gt; {
  window.addEventListener("resize", handleResize);
  handleResize();
  handleDraw();
});

onBeforeUnmount(() =&amp;gt; {
  window.removeEventListener("resize", handleResize);
});

watch(
  () =&amp;gt; width.value,
  () =&amp;gt; {
    remove();
    handleDraw();
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid having multiple charts when the page re-renders, we can create a function that checks if the number of charts on the page exceeds one. If true, it will purge all except one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const remove = () =&amp;gt; {
  // TODO: Get svg reference
  const svg = d3.select(barChartRef.value).selectAll("svg");

  // check the number of existing elements, if greater than 0; remove all existing ones
  if (svg.size()) svg.remove().exit();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can make the chart to be responsive for every device screen by checking the user's screen size and displaying a chart within the width of the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleResize = () =&amp;gt; {
  if (barChartRef?.value?.offsetWidth) {
    width.value = barChartRef.value.offsetWidth - margin.left - margin.right;
    return;
  }

  if (window.innerWidth &amp;lt; 400) {
    width.value = 300 - margin.left - margin.right;
    return;
  }

  width.value = 550 - margin.left - margin.right;
  return;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let us create a function that with generate our chart every time the page renders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleDraw = async () =&amp;gt; {
  // append the svg object to the body of the page
  const svg = d3
    .select(barChartRef.value)
    .append("svg")
    .attr("width", width.value + margin.left + margin.right)
    .attr("height", height.value + margin.top + margin.bottom)
    .append("g")
    .attr("transform", `translate(${margin.left},${margin.top})`);

  // Add X axis
  const x = d3
    .scaleBand()
    .domain(data.value.map((d) =&amp;gt; d.label))
    .range([0, width.value])
    .padding(0.2);

  svg
    .append("g")
    .attr("transform", `translate(0, ${height.value})`)
    .call(d3.axisBottom(x))
    .selectAll("text")
    .attr("transform", "translate(-10,0)rotate(-45)")
    .style("text-anchor", "end");

  // Add Y axis
  const y = d3
    .scaleLinear()
    .domain([0, d3.max(data.value, (d): number =&amp;gt; d.value)] as number[])
    .range([height.value, 0]);

  svg.append("g").call(d3.axisLeft(y));

  // Bars
  svg
    .selectAll("mybar")
    .data(data.value)
    .enter()
    .append("rect")
    .attr("x", (d) =&amp;gt; x(d.label) as number)
    .attr("y", (d) =&amp;gt; y(d.value))
    .attr("width", x.bandwidth())
    .attr("height", (d) =&amp;gt; height.value - y(d.value))
    .attr("fill", "#4682b4");

  svg
    .append("text")
    .attr("class", "title")
    .attr("x", width.value / 2)
    .attr("y", 0 - margin.top / 2)
    .attr("text-anchor", "middle")
    .text("Company Revenue in USD ($)");
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we added both the &lt;code&gt;x-axis&lt;/code&gt; and &lt;code&gt;y-axis&lt;/code&gt; components. First, we defined both axes, then we formatted them to fit into our chart using the following code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Add X axis
  const x = d3
    .scaleBand()
    .domain(data.value.map((d) =&amp;gt; d.label))
    .range([0, width.value])
    .padding(0.2);

  svg
    .append("g")
    .attr("transform", `translate(0, ${height.value})`)
    .call(d3.axisBottom(x))
    .selectAll("text")
    .attr("transform", "translate(-10,0)rotate(-45)")
    .style("text-anchor", "end");

  // Add Y axis
  const y = d3
    .scaleLinear()
    .domain([0, d3.max(data.value, (d): number =&amp;gt; d.value)] as number[])
    .range([height.value, 0]);

  svg.append("g").call(d3.axisLeft(y));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A sample output should look like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aic1LSSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vhce1nz6pejklewvrb5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aic1LSSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vhce1nz6pejklewvrb5.PNG" alt="Vue graph" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is all we need to do in order to create a real time responsive and dynamic visualization using D3.js and Vue.js. The repository for this tutorial is on &lt;a href="https://github.com/callezenwaka/graphs/tree/main/vue-chart"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>d3</category>
      <category>vue</category>
      <category>web</category>
    </item>
    <item>
      <title>Deploy Node.js and Vue.js as Multi-Service Applications on the same Google App Engine.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Mon, 10 Apr 2023 17:48:58 +0000</pubDate>
      <link>https://dev.to/callezenwaka/deploy-multi-service-applications-on-google-app-engine-41l8</link>
      <guid>https://dev.to/callezenwaka/deploy-multi-service-applications-on-google-app-engine-41l8</guid>
      <description>&lt;p&gt;Many startups fall into the trap of premature optimization, investing excessive resources in complex infrastructure like microservices before validating their business model. This misalignment of priorities diverts precious engineering time away from customer acquisition and revenue generation, often leading to technically impressive but commercially unsuccessful ventures.&lt;/p&gt;

&lt;p&gt;Google App Engine (GAE) offers a compelling alternative as a serverless platform that handles scalability, security, and reliability concerns automatically. GAE enables organizations to focus primarily on their business logic and core value proposition, rather than operational complexities by abstracting away infrastructure management.&lt;/p&gt;

&lt;p&gt;The challenge becomes particularly evident in polyglot architectures where applications are developed using different programming languages and frameworks. Traditional approaches might require manually managing containers or virtual machines for each component, adding significant operational overhead and complexity.&lt;/p&gt;

&lt;p&gt;GAE provides an elegant solution by allowing multiple applications—such as a Vue.js client interface, React dashboards, Python analytics and Node.js backendl, to be deployed within a single project. This approach maintains technological flexibility while dramatically reducing the operational burden, even as the architecture expands to include recommendation engines, analytics services, and other specialized components.&lt;/p&gt;

&lt;p&gt;For early-stage businesses, the priority should be building "extensible enough" infrastructure that supports rapid iteration and learning until the business model is proven. Only after achieving product-market fit and sustainable revenue does it make sense to invest heavily in more sophisticated technical foundations—and even then, managed platforms often remain the most efficient solution.&lt;/p&gt;

&lt;p&gt;Let us start by virtualizing our client project tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── app.yaml
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── vite.svg
├── src
└── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;node_modules&lt;/code&gt; directory is redacted to maximize space. If we are looking at the project tree, there is an &lt;code&gt;app.yaml&lt;/code&gt; file that contains the GAE commands and required application parameters as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Runtime engine nodejs 18 LTS
runtime: nodejs18
service: default
handlers:
  # Serve all static files with urls ending with a file extension
- url: /(.*\..+)$ 
  static_files: dist/\1
  upload: dist/(.*\..+)$
  # catch all handler to index.html
- url: /.*
  static_files: dist/index.html
  upload: dist/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has the &lt;code&gt;runtime&lt;/code&gt;, &lt;code&gt;service&lt;/code&gt; and &lt;code&gt;handler&lt;/code&gt; for routing request. It is worth mentioning that the first deployed application takes default as it's service name and might throw an error if this is skipped. You can read &lt;a href="https://cloud.google.com/appengine/docs/standard" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Navigate to the google cloud console and configure the cloudbuild service account &lt;code&gt;xxxxxxxxxxxxxx@cloudbuild.gserviceaccount.com&lt;/code&gt; by add &lt;code&gt;App Engine Deployer&lt;/code&gt;, &lt;code&gt;App Engine Deployer&lt;/code&gt;, and &lt;code&gt;Storage Object Viewer&lt;/code&gt; roles.&lt;/p&gt;

&lt;p&gt;Next, let's build our Vue.js project by running the command &lt;code&gt;npm run build&lt;/code&gt;. Then, we will deploy the application by running the following command &lt;code&gt;gcloud app deploy --project &amp;lt;project-name&amp;gt;&lt;/code&gt; from the root directory. Once the deploy is successful, navigate to &lt;code&gt;https://&amp;lt;project-name&amp;gt;.&amp;lt;region_id&amp;gt;.r.appspot.com/&lt;/code&gt; to access the default application. &lt;/p&gt;

&lt;p&gt;To deploy the node.js server service within the same project, we follow the same process. First, the project tree looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── app.yaml
├── controllers
│   └── user.js
├── database
│   └── index.js
├── index.js
├── lib
│   └── auth.js
├── migrate.js
├── node_modules
├── package-lock.json
├── package.json
└── routes
    └── user.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple and typical Node.js application and our interest is the &lt;code&gt;app.yaml&lt;/code&gt; file which contains the following key-value pairs &lt;code&gt;runtime: nodejs18&lt;/code&gt; and &lt;code&gt;service: server&lt;/code&gt;. As above, we will deploy the application by running the following command &lt;code&gt;gcloud app deploy --project &amp;lt;project-name&amp;gt;&lt;/code&gt; from the root directory. And visit the application by navigating to &lt;code&gt;https://&amp;lt;service-name&amp;gt;-dot-&amp;lt;project-name&amp;gt;.&amp;lt;region_id&amp;gt;.r.appspot.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One final thing that we might need to add is mapping a domain name to these services. To do that, we can a &lt;code&gt;dispatch.yaml&lt;/code&gt; file to either the client or server application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dispatch:
- url: "www.client.com/*"
  service: default
- url: "client.com/*"
  service: default
- url: "server.com/*"
  service: server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Client application is preferable as it is the default application. Worthy to mention is that we can separate the content of the dispatch file into respective application, the choice is yours. Ensure that you have added all the domain and subdomain in the &lt;code&gt;custom domain&lt;/code&gt; tab of the App Engine.&lt;/p&gt;

&lt;p&gt;Then deploy the &lt;code&gt;dispatch.yaml&lt;/code&gt; file by running the command &lt;code&gt;gcloud app deploy dispatch.yaml&lt;/code&gt;. If all deployments are successful, visiting any of the urls in the dispatch file should open any of the mapped application.&lt;/p&gt;

&lt;p&gt;Again, we just deployed only two services, but it could have been more to fit our peculiarities. The repository for this tutorial is on &lt;a href="https://github.com/callezenwaka/demos" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>javascript</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Use Firebase Auth to Manage User Permissions and Enforce Principle of Least Privilege on API Endpoints. Part 2.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Mon, 10 Apr 2023 11:45:31 +0000</pubDate>
      <link>https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-2-29hn</link>
      <guid>https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-2-29hn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-1-16if"&gt;part 1&lt;/a&gt; of User Permissions and the Principle of Least Privilege on API Endpoints using Firebase, we explained the need for managing user authentication and authorization using roles and permissions.&lt;/p&gt;

&lt;p&gt;We also applied the principle to various server endpoints. Here, we are jumping straight into client implementation using &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;, An approachable, performant and versatile framework for building web user interfaces.&lt;/p&gt;

&lt;p&gt;A quick display of the project tree is depicted below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.vue
│   ├── assets
│   │   ├── avatar.png
│   │   └── vue.svg
│   ├── components
│   │   ├── core
│   │   │   └── Header.vue
│   │   ├── generics
│   │   └── user
│   │       ├── User.vue
│   │       ├── UserCard copy.vue
│   │       ├── UserCard.vue
│   │       ├── UserCreate.vue
│   │       ├── UserSkeleton.vue
│   │       ├── UserUpdate.vue
│   │       └── Users.vue
│   ├── config
│   │   └── index.js
│   ├── database
│   │   └── index.js
│   ├── firebase
│   │   └── index.js
│   ├── main.js
│   ├── router
│   │   └── index.js
│   ├── service
│   │   └── index.js
│   ├── store
│   │   ├── index.js
│   │   └── modules
│   │       └── user.js
│   ├── style.css
│   ├── utils
│   │   └── index.js
│   └── views
│       ├── Dashboard.vue
│       ├── Login.vue
│       ├── NotFound.vue
│       └── Register.vue
└── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike in the server side, the client side is straight forward as most of the heavy lifted has already been performed on the server. However, few directories deserve a mention: &lt;code&gt;components&lt;/code&gt;, &lt;code&gt;config&lt;/code&gt;, &lt;code&gt;firebase&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;utils&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The component directory has all the various pages for performing &lt;code&gt;CRUD&lt;/code&gt; operations on the client side. The config directory has &lt;code&gt;axios&lt;/code&gt; implementation for making requests and getting responses from the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const request = async (url, method, token, payload, query) =&amp;gt; {
  return await axios({
    method: method,
    url: `${url}`,
    data: payload,
    params: query,
    headers: {
      'Accept': 'application/json',
      'Authorization': `Bearer ${store.getters.idToken}`,
    },
    json: true,
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, the firebase directory has the &lt;code&gt;Firebase configuration object&lt;/code&gt; containing keys and identifiers for our application. &lt;br&gt;
The store directory has, well, the &lt;code&gt;Veux&lt;/code&gt; store that manages our data state and help centralize the implementation of requests to API endpoints. We will come back to Veux store shortly.&lt;/p&gt;

&lt;p&gt;And finally, the utils directory that has our custom defined functions. Within this file, we defined the &lt;code&gt;isAuhorised&lt;/code&gt; method that together with firebase &lt;code&gt;onAuthStateChanged&lt;/code&gt; supports the re-validation of user &lt;code&gt;token&lt;/code&gt; and &lt;code&gt;claims&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * check user authorisation
 * @param {string} action 
 * @returns {boolean}  
 */
export const isAuhorised = (action) =&amp;gt; {
  // TODO: get current user permissions
  const { permissions } = store.getters.profile;
  // TODO: verify if user has permission for the intended action
  return permissions.some(permission =&amp;gt; permission.name === action &amp;amp;&amp;amp; permission.value === true);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main.js file serves as the entry point to our client application. Here, we use the &lt;code&gt;onAuthStateChanged&lt;/code&gt; method from the &lt;code&gt;firebase/auth&lt;/code&gt; to identify and setup the current signed in user, authentication status, role and permissions custom claims, and &lt;code&gt;id token&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onAuthStateChanged(auth, async user =&amp;gt; {
  if (user &amp;amp;&amp;amp; user.emailVerified) {
    const idTokenResult = await user.getIdTokenResult();
    const { claims: { entity, name, permissions } } = await user.getIdTokenResult();

    if (idTokenResult &amp;amp;&amp;amp; idTokenResult.claims) {
      await store.dispatch('setProfile', { ...user, entity, name, permissions });
      await store.dispatch('setClaims', idTokenResult.claims);
      await store.dispatch('setIsAuthenticated', true);
      await store.dispatch('setIdToken', idTokenResult.token);
      await store.dispatch('setCurrentUser', idTokenResult.claims.entity);
    }
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, within the Veux store, we will use &lt;code&gt;user.js&lt;/code&gt; as the entry point for invoking the user endpoints. To be sure that a user is granted only the necessary access needed to execute a task, we are going to use the &lt;code&gt;isAuhorised&lt;/code&gt; function. The necessary action is passed as a parameter which either allow or deny requests to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(!isAuhorised(action)) return;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, when a user tries to make an API call to the &lt;code&gt;getUsers&lt;/code&gt; endpoint, we can check if the user has enough privilege to continue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  async getUsers(context, payload) {
    try {
      // TODO: check user permission
      if(!isAuhorised('readAll')) return;
      // TODO: api call
      await context.dispatch('setLoading', true);
      if (!payload &amp;amp;&amp;amp; context.state.users &amp;amp;&amp;amp; !!context.state.users.length) {
        await context.dispatch('setLoading', false);
        return context.state.users;
      }
      const { data } = await userApi.getUsers(context.rootGetters.idToken);
      if (!Array.isArray(data)) return;
      await context.dispatch('setLoading', false);
      context.commit('SET_USERS', data);
      return data;
    } catch (error) {
      console.log('Error: ', error);
      return;
    }
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we are making sure that users do not attempt a request unless they have the permission to do so. The repository for this tutorial is on &lt;a href="https://github.com/callezenwaka/demos/tree/master/vue-client"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

&lt;p&gt;Reference:&lt;br&gt;
&lt;a href="https://www.cyberark.com/what-is/least-privilege/"&gt;https://www.cyberark.com/what-is/least-privilege/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>javascript</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Use Firebase Auth to Manage User Permissions and Enforce Principle of Least Privilege on API Endpoints. Part 1.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Mon, 10 Apr 2023 10:35:47 +0000</pubDate>
      <link>https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-1-16if</link>
      <guid>https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-1-16if</guid>
      <description>&lt;p&gt;The principle of least privilege is a foundational component of zero trust frameworks. These sets of frameworks advocate that organizations should not automatically trust activities within or outside their infrastructure perimeters.&lt;/p&gt;

&lt;p&gt;It demands that organizations setup and deploy stringent authentication and authorization protocols and grant only the necessary access needed for executing a task to devices that connect to either their remote or on-premises systems. &lt;/p&gt;

&lt;p&gt;Many organizations are accelerating their digital transformation strategies, thus shifting from traditional perimeter security approaches to the Zero Trust framework to protect their most sensitive networks.&lt;/p&gt;

&lt;p&gt;This piece will focus on fine-grain permissions of server endpoints using &lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt;, an app development platform that helps you build and grow apps and games users love. We have &lt;a href="https://nodejs.org/en"&gt;Node.js®&lt;/a&gt;, an open-source, cross-platform JavaScript runtime environment. Check out the &lt;a href="https://dev.to/callezenwaka/user-permissions-and-the-principle-of-least-privilege-on-api-endpoints-using-firebase-part-2-29hn"&gt;part 2&lt;/a&gt; here.&lt;/p&gt;

&lt;p&gt;One might ask, why is the Principle of Least Privilege important? Some answers are provided below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It reduces the cyber-attack surface.&lt;/li&gt;
&lt;li&gt;It stops the spread of malware.&lt;/li&gt;
&lt;li&gt;It improves end-user productivity.&lt;/li&gt;
&lt;li&gt;It helps streamline compliance and audits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simple project setup will be used to make the tutorial as basic as possible. However, the same approach could be extended and applied to more complex Enterprise SaaS applications. The code tree is as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── controllers
│   └── user.js
├── database
│   └── index.js
├── index.js
├── lib
│   └── auth.js
├── migrate.js
├── package-lock.json
├── package.json
├── permission.json
└── routes
    └── user.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have four directories viz: &lt;code&gt;controllers&lt;/code&gt;, &lt;code&gt;routes&lt;/code&gt;, &lt;code&gt;database&lt;/code&gt;and &lt;code&gt;lib&lt;/code&gt;. As already mentioned, the setup is simplified by using a single &lt;strong&gt;user&lt;/strong&gt; route. The controller directory has the &lt;code&gt;user.js&lt;/code&gt; file with the endpoint callback functions. While the routes directory has the various endpoints to perform &lt;code&gt;CRUD&lt;/code&gt; operations.&lt;/p&gt;

&lt;p&gt;The lib directory has the &lt;code&gt;auth.js&lt;/code&gt; file for the authentication related functions. Other files are the &lt;code&gt;index.js&lt;/code&gt; file, which as we know is the entry point for the project. The &lt;code&gt;permission.json&lt;/code&gt; is another important file as it contains the firebase service account settings.&lt;/p&gt;

&lt;p&gt;The database has the mock data with a user role. The user role is assigned to a user using the &lt;code&gt;setCustomUserClaims&lt;/code&gt; of the firebase &lt;code&gt;getAuth&lt;/code&gt; method. The mock data consist of user object with the following schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    id: '1',
    username: 'janedoe',
    displayName: 'Jane Doe',
    email: 'jane.doe@permission.com',
    password: 'password',
    phoneNumber: '+2348030004001',
    photoURL: 'http://avatar.png',
    emailVerified: true,
    disabled: false,
    role: {
      name: "User",
      entity: "isUser",
      permissions: [
        { name: "create", value: false, },
        { name: "readAll", value: false },
        { name: "read", value: true, },
        { name: "update", value: true, },
        { name: "delete", value: false, },
      ],
    },
    created_at: 1673431871000,
    updated_at: 1673431871000,
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the role variable has the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;entity&lt;/code&gt;, &lt;code&gt;permissions&lt;/code&gt; etc. The permissions variable is also an array of key-value pairs of &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;, the fine-grained access permissions. The name is the intended action while the value is the privilege to perform such action.&lt;/p&gt;

&lt;p&gt;Then, there is the &lt;code&gt;migration.js&lt;/code&gt; file which is used to seed data to firebase authentication table. And finally, the &lt;code&gt;package.json&lt;/code&gt; file, which has the script object for starting and migrating the mock data. To migrate the mock data, run the command &lt;code&gt;npm run migration up&lt;/code&gt;. (N.B: Make sure that you do not skip the last &lt;em&gt;up&lt;/em&gt; word in the command).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "scripts": {
    "start": "node index.js",
    "dev": "nodemon -r dotenv/config index.js",
    "migration": "node migrate.js",
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project set has five endpoints: &lt;code&gt;getUsers&lt;/code&gt;, &lt;code&gt;getUser&lt;/code&gt;, &lt;code&gt;addUser&lt;/code&gt;, &lt;code&gt;updateUser&lt;/code&gt;, and &lt;code&gt;deleteUser&lt;/code&gt;. Each of these endpoints has a corresponding action: &lt;code&gt;readAll&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt; respectively. As previously stated, the simplicity of this implementation is for demonstration purposes.&lt;/p&gt;

&lt;p&gt;The auth.js file has two major function definitions. The &lt;code&gt;isAuthenticated&lt;/code&gt; function for authentication and the &lt;code&gt;isUser&lt;/code&gt; function for authorization. This role is added to the account custom claim during the account creation step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * [START CHECK USER]
 * @param {object} req Express request context.
 * @param {object} res Express response context.
 * @param {object} next Express next context.
 */
exports.isUser = (action) =&amp;gt; (req, res, next) =&amp;gt; {
    getAuthToken(req, res, async () =&amp;gt; {
        try {
            // TODO: get and verify authToken
            const { authToken } = req;
            const userInfo = await getAuth().verifyIdToken(authToken);
            // TODO: user has permission required to perform action on any API method
            const allow = userInfo.permissions.some(permission =&amp;gt; permission.name === action &amp;amp;&amp;amp; permission.value === true);
            // TODO: user not allowed to take action
            if (!allow) return res.status(403).json('Forbidden access!!');
            // TODO: user allowed to take action
            req.user = userInfo;
            return next();
        }
        catch (error) {
            return res.status(401).json('Unauthorized access!');
        }
    });
}
// [END CHECK USER]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, I am certain, is very explanatory. The &lt;code&gt;action&lt;/code&gt; parameter is used to determine the intended task and if a user making a request or expecting a response has the right permission. These permissions and the corresponding authentication function is used to authenticate and authorize requests and responses.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;isAuthenticated&lt;/code&gt; is passed as application middleware within &lt;code&gt;index.js&lt;/code&gt; to authenticate requests as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Verify request middleware
app.use('/', isAuthenticated);
// Private routes
app.use('/api/v1/user', user);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the &lt;code&gt;isUser&lt;/code&gt; role with its associated permission is passed before the callback function as a middleware on every route endpoint to authorize user actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

// import packages and dependencies
const user = require('../controllers/user');
const { isUser } = require('../lib/auth');
const express = require('express');
const router = express();

router.get('/', isUser('readAll'), user.getUsers); 

router.get('/:id', isUser('read'), user.getUser);

router.post('/', isUser('create'), user.addUser);

router.put('/', isUser('update'), user.updateUser);

router.delete('/:id', isUser('delete'), user.deleteUser);

module.exports = router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much about setting up the Principle of Least Privilege on API Endpoints on the server. Next, we will cover how to hook it up with client to authenticate and authorize a user to perform an action. The repository for this tutorial is on &lt;a href="https://github.com/callezenwaka/demos/tree/master/node-server"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

&lt;p&gt;Reference:&lt;br&gt;
&lt;a href="https://www.cyberark.com/what-is/least-privilege/"&gt;https://www.cyberark.com/what-is/least-privilege/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>javascript</category>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Automating Blockchain Build and Integration with Node.js.</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Wed, 29 Mar 2023 19:01:36 +0000</pubDate>
      <link>https://dev.to/callezenwaka/setting-up-blockchain-deployment-in-nodejs-4i3p</link>
      <guid>https://dev.to/callezenwaka/setting-up-blockchain-deployment-in-nodejs-4i3p</guid>
      <description>&lt;p&gt;When implementing a blockchain solution using Node.js and Solidity, developers often need to copy the ABI code during deployment. This process often happens before commit to a remote repository.&lt;/p&gt;

&lt;p&gt;In this article, we shall see a quick trick that achieves the same result without breaking a sweat. Before delving into the details, let's run a quick project setup with hardhat. For more info, &lt;a href="https://hardhat.org/hardhat-runner/docs/getting-started#overview" rel="noopener noreferrer"&gt;see&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a start, run the basic installation of hardhat &lt;code&gt;npm install --save-dev hardhat&lt;/code&gt; if not previously done. Setup a new project with this command &lt;code&gt;npx hardhat&lt;/code&gt; and follow the prompt.&lt;/p&gt;

&lt;p&gt;The use-case for your blockchain application will differ from the one elaborate here. Be sure to run &lt;code&gt;npm install &amp;lt;package name&amp;gt;&lt;/code&gt; to install packages that are peculiar to your use-case.&lt;/p&gt;

&lt;p&gt;The project folder structure or tree should look like below image after setup:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzizuv1hqdbvomwpy0544.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzizuv1hqdbvomwpy0544.PNG" alt="Project Tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can see all the directories especially the &lt;code&gt;scripts&lt;/code&gt; and &lt;code&gt;src/abi&lt;/code&gt;. Both directories will be handy in our implementations. Ensure that the file(s) for your solidity code are inside the &lt;code&gt;contract&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;Now, this is where the magic happens. Create a new script file &lt;code&gt;files.ts&lt;/code&gt; inside the scripts directory and add the below code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import fs from "fs";
import path from "path";

const folderList: string[] = [];
function output(folderPaths: string[]) {
  folderPaths.forEach(folderPath =&amp;gt; {
    const results = fs.readdirSync(folderPath);
    const folders = results.filter((res: any) =&amp;gt; fs.lstatSync(path.resolve(folderPath, res)).isDirectory());
    const innerFolderPaths = folders.map((folder: any) =&amp;gt; path.resolve(folderPath, folder));
    if(innerFolderPaths.length === 0) {
      return;
    };
    innerFolderPaths.forEach((innerFolderPath: string) =&amp;gt; {
      fs.readdirSync(innerFolderPath).forEach((file: string) =&amp;gt; {
        if (file.split('.')[1] === 'json') {
          fs.rename(`${innerFolderPath}/${file}`, `./src/abi/${file}`, function (err: any) {
            if (err) {
              throw err;
            }
          })
        }
      });
      folderList.push(innerFolderPath)
    });
    output(innerFolderPaths);
  });
}

output([path.resolve(__dirname, "../artifacts/contracts")]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code uses recursion and &lt;code&gt;fileSystem&lt;/code&gt; native API to loop through all the directories in a given directory, in this case &lt;code&gt;../artifacts/contracts&lt;/code&gt;. Then, open the &lt;code&gt;package.json&lt;/code&gt; file and add the following piece of code inside the scripts object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"files": "ts-node ./scripts/files.ts",
"compile": "npx hardhat compile &amp;amp;&amp;amp; npm run files",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here is that once you run &lt;code&gt;npm run compile&lt;/code&gt; command to compile your solidity smart contract, it will run the script &lt;code&gt;./scripts/files.ts&lt;/code&gt;. Afterward, it will copy the ABI codes for all the solidity code found inside the contract directory to the &lt;code&gt;src/abi&lt;/code&gt; directory as shown below.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h6oz1wveo2yxsf93hp8.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h6oz1wveo2yxsf93hp8.PNG" alt="Abi folder tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this simple automation, there is no need to manually copy the &lt;code&gt;ABIs&lt;/code&gt; compiled smart contracts to your project. The repository for this tutorial is on &lt;a href="https://github.com/callezenwaka/onevault_api" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>solidity</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Understanding Blockchain using JavaScript Class</title>
      <dc:creator>Callis Ezenwaka</dc:creator>
      <pubDate>Mon, 20 Mar 2023 19:08:59 +0000</pubDate>
      <link>https://dev.to/callezenwaka/understanding-blockchain-using-javascript-class-1lak</link>
      <guid>https://dev.to/callezenwaka/understanding-blockchain-using-javascript-class-1lak</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This tutorial will demonstrate how to simulate and run a blockchain system using JavaScript. Often times, whenever blockchain and to some extent smart contract is described, what comes to minds is an opaque black-box. It could be that the predominant language used for writing most smart contract codes contributes to this.&lt;/p&gt;

&lt;p&gt;This piece will show a detailed step-by-step approach to implementing a smart contract using Node.js and vanilla JavaScript. A test suite will also be added to test the whole application. The source code for the article can be found in this &lt;a href="https://github.com/callezenwaka/blockchain" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. Let's get started!&lt;/p&gt;

&lt;p&gt;Here, we have three parameters, two arrays representing &lt;code&gt;initialBalances&lt;/code&gt; and &lt;code&gt;transactions&lt;/code&gt; and a third integer representing the &lt;code&gt;blockSize&lt;/code&gt;. We are going to create a blockchain smart contract that includes all valid pending transactions in the order in which they are given. And provide functionality to get the balance of a specific account on the blockchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation of Dependencies
&lt;/h2&gt;

&lt;p&gt;First, clone the above repository and run &lt;code&gt;npm install&lt;/code&gt; and skip over to the &lt;strong&gt;Implementation&lt;/strong&gt; section below. If you are otherwise coding along, initialize your node.js project by running &lt;code&gt;npm init -y&lt;/code&gt;. Then, create two files &lt;code&gt;blockchain.js&lt;/code&gt; and &lt;code&gt;blockchain.test.js&lt;/code&gt;. Install the following dependencies by running &lt;code&gt;npm i sha1 chai mocha&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The files in the directory should look like the below image:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj59evvw7p42tksx5iq9m.PNG" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj59evvw7p42tksx5iq9m.PNG" alt="File directory" width="800" height="564"&gt;&lt;/a&gt;&lt;br&gt;
Then add the following codes to the &lt;code&gt;blockchain.js&lt;/code&gt; file &lt;a href="https://github.com/callezenwaka/blockchain/blob/main/blockchain.js" rel="noopener noreferrer"&gt;blockchain.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the file and add the following code to the &lt;code&gt;blockchain.test.js&lt;/code&gt; file &lt;a href="https://github.com/callezenwaka/blockchain/blob/main/blockchain.test.js" rel="noopener noreferrer"&gt;blockchain.test.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the file also and open the &lt;code&gt;package.json&lt;/code&gt; file. Here, you need to make minor changes to the script commands. First, the following code to the script part of the &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "start": "node blockchain.js",
    "test": "mocha --timeout 10000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--timeout 10000&lt;/code&gt; will ensure that all the test will pass during testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;There are two interfaces viz: &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;getAccountBalance&lt;/code&gt;. But before that, let's define some parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const balances = [200, 250, 500];
  const transactions = [[0, 1, 50], [1, 2, 80], [1, 0, 100], [1, 2, 600], [2, 0, 150], [1, 0, 50], [2, 1, 60], [0, 1, 55], [1, 2, 40], [1, 0, 70]];
  const blockSize = 3;
  const index = 1;
  const pendingTransactions = [[0,1,50], [1,2,80]];
  const prevBlockHash = '548bc92f827bd41ad97243cc3ca4b765f8c68a2c';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to execute the code and run the test. Run the following code sequentially inside the root directory of the project (Ignore the undefined output after executing any of the commands):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Activate the Node REPL with &lt;code&gt;node&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initialize the constructor by running &lt;code&gt;const Blockchain = require('./blockchain');&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instantiate an object by running &lt;code&gt;const blockchain = new Blockchain();&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initialize an object by running &lt;code&gt;blockchain.init(balances, transactions, blockSize);&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrieve account balance by running &lt;code&gt;blockchain.getAccountBalance(index);&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Node REPL should look like the following image if everything checks out: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12ofjd7tofg8osdmy41r.PNG" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12ofjd7tofg8osdmy41r.PNG" alt="Node REPL" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Codes
&lt;/h2&gt;

&lt;p&gt;Exit the Node REPL and return to the root directory. Run the test by executing &lt;code&gt;npm test ./blockchain.test.js&lt;/code&gt;. The output should look like below image:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhue88705yy7t10hqpte.PNG" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhue88705yy7t10hqpte.PNG" alt="Testing output" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have successfully implemented and tested a blockchain code using JavaScript. You can take some time to go through the code in order to understand how each part of the code interact with each other.&lt;/p&gt;

&lt;p&gt;If you like the article, do like and share with friends.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>testing</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
