<?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>Forem: Desmond Obisi</title>
    <description>The latest articles on Forem by Desmond Obisi (@desmondsanctity).</description>
    <link>https://forem.com/desmondsanctity</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%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg</url>
      <title>Forem: Desmond Obisi</title>
      <link>https://forem.com/desmondsanctity</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/desmondsanctity"/>
    <language>en</language>
    <item>
      <title>Cron but with AI insights, telemetry data in one command directly from your codebase.</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 10 Aug 2025 23:37:10 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-2cj1</link>
      <guid>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-2cj1</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/desmondsanctity" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg" alt="desmondsanctity"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis&lt;/h2&gt;
      &lt;h3&gt;Desmond Obisi ・ Aug 10&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#redischallenge&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devchallenge&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#database&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Cron but with AI insights, telemetry data in one command directly from your codebase...</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 10 Aug 2025 23:36:49 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-4dbl</link>
      <guid>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-4dbl</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-story__hidden-navigation-link"&gt;Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Redis AI Challenge: Beyond the Cache&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/desmondsanctity" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg" alt="desmondsanctity profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/desmondsanctity" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Desmond Obisi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Desmond Obisi
                
              
              &lt;div id="story-author-preview-content-2766562" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/desmondsanctity" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Desmond Obisi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 10 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" id="article-link-2766562"&gt;
          Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/redischallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;redischallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;19&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Cron but with AI insights, telemetry data in one command directly from your codebase</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 10 Aug 2025 23:36:06 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-4f54</link>
      <guid>https://forem.com/desmondsanctity/cron-but-with-ai-insights-telemetry-data-in-one-command-directly-from-your-codebase-4f54</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-story__hidden-navigation-link"&gt;Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Redis AI Challenge: Beyond the Cache&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/desmondsanctity" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg" alt="desmondsanctity profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/desmondsanctity" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Desmond Obisi
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Desmond Obisi
                
              
              &lt;div id="story-author-preview-content-2766562" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/desmondsanctity" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F369612%2Fc4a2ee97-1bda-46ce-8884-b4598cb20fd3.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Desmond Obisi&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 10 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" id="article-link-2766562"&gt;
          Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/redischallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;redischallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;19&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Chronos Synapse — BYOK Telemetry + AI Insights SDK For Cron Using Redis</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 10 Aug 2025 23:34:30 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5</link>
      <guid>https://forem.com/desmondsanctity/chronos-synapse-byok-telemetry-ai-insights-sdk-for-cron-using-redis-47l5</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/redis-2025-07-23"&gt;Redis AI Challenge&lt;/a&gt;: Beyond the Cache.&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Chronos Synapse is a telemetry-first job analytics platform that turns your scheduled/triggered code into real-time operational insight — without running code on the server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SDK-first ingestion: a lightweight Node SDK (&lt;code&gt;chronos-synapse-sdk&lt;/code&gt;) that registers jobs and streams execution telemetry (status, duration, errors, stdout/stderr, code snippet/language, versions).&lt;/li&gt;
&lt;li&gt;Backend ingestion service: Fastify API that stores job definitions and execution events in Redis Stack and emits real-time updates via Socket.IO.&lt;/li&gt;
&lt;li&gt;Read-only dashboard: a Next.js 14 app for visualizing jobs, executions, and analytics with live updates and user-scoped stats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this approach? Many teams already run jobs in their own apps. Chronos keeps the execution where it belongs and specializes in visibility, metrics, and real-time insights.&lt;/p&gt;

&lt;p&gt;Key capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deterministic job IDs and schedules (cron or one-time &lt;code&gt;runAt&lt;/code&gt;) with server-driven &lt;code&gt;job:trigger&lt;/code&gt; events for recurring jobs.&lt;/li&gt;
&lt;li&gt;Rich telemetry capture out of the box (error stack, stdout/stderr, snippet+language, app and job versions) with batching, backoff, and retries.&lt;/li&gt;
&lt;li&gt;Real-time dashboard updates powered by Redis Pub/Sub → Socket.IO.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chronos-synapse.vercel.app" rel="noopener noreferrer"&gt;Live Dashboard (read-only)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chronos-synapse-backend.fly.dev/api/health" rel="noopener noreferrer"&gt;Public API (ingestion not open; demo endpoints)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/chronos-synapse-sdk" rel="noopener noreferrer"&gt;SDK package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/lyIFkDLddgc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Screenshots (replace with your links):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Jobs list with “Last run” indicators&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%2Fte7spfkv7quhj2lhnjks.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%2Fte7spfkv7quhj2lhnjks.png" alt="Chronos Job List"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execution details with inline logs &amp;amp; code snippet&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%2Ffc73prsmklbwoy00tp29.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%2Ffc73prsmklbwoy00tp29.png" alt="Chronos Execution Details"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Analytics with success/failed stacked bars + total executions line&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%2F91hl7z7csisp45ce5wsz.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%2F91hl7z7csisp45ce5wsz.png" alt="Chronos Analytics"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick SDK example:&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="nx"&gt;ChronosRunner&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;chronos-synapse-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChronosRunner&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHRONOS_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;// endpoint defaults to http://localhost:3001&lt;/span&gt;
 &lt;span class="na"&gt;captureConsole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;registerJobs&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job:daily-report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Daily Report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 * * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;runMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recurring&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job:launch-once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One-time Launch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;runMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;runAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2025-09-01T12:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job:daily-report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// your work&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job:launch-once&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// runs once based on runAt or first cron match&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Redis 8
&lt;/h2&gt;

&lt;p&gt;Chronos Synapse leans on Redis Stack as the system of record and real-time transport — far beyond caching. We combine multiple modules/features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RedisJSON (primary store)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jobs and executions are stored as JSON documents. Execution JSON includes rich telemetry fields (stdout/stderr, errorType/stack, code snippet/language, versions, labels/metadata).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;RediSearch (fast querying &amp;amp; analytics)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two indices: &lt;code&gt;idx:jobs&lt;/code&gt; and &lt;code&gt;idx:executions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Executions index uses a TAG field for &lt;code&gt;jobId&lt;/code&gt; (normalized, hyphens removed) so we can query with &lt;code&gt;@jobId:{id1|id2|...}&lt;/code&gt; per user scope.&lt;/li&gt;
&lt;li&gt;We sort/filter by &lt;code&gt;startedAt&lt;/code&gt; and aggregate client-side for charts and daily/weekly/monthly bucketing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;RedisTimeSeries (metrics and trends)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time series keys for counters and duration distributions: &lt;code&gt;ts:jobs:executions&lt;/code&gt;, &lt;code&gt;ts:jobs:success&lt;/code&gt;, &lt;code&gt;ts:jobs:failed&lt;/code&gt;, &lt;code&gt;ts:jobs:duration_ms&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Writes use &lt;code&gt;TS.INCRBY&lt;/code&gt; (counters) and &lt;code&gt;TS.ADD ... ON_DUPLICATE LAST&lt;/code&gt; (durations) with &lt;code&gt;DUPLICATE_POLICY=LAST&lt;/code&gt; to avoid collisions.&lt;/li&gt;
&lt;li&gt;This enables a fast, low-overhead trend API for the analytics page.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Pub/Sub (realtime fan-out)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingestion publishes a lightweight &lt;code&gt;execution:ingested&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;A managed Redis subscriber relays events to Socket.IO (with keepalive &lt;code&gt;PING&lt;/code&gt; every 20s, auto-reinit on failure).&lt;/li&gt;
&lt;li&gt;Dashboard receives instant updates for “Recent Executions” and stats.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Operational hardening (Redis client)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;connectTimeout&lt;/code&gt;, &lt;code&gt;keepAlive&lt;/code&gt;, &lt;code&gt;noDelay&lt;/code&gt;, and a reconnect strategy that avoids 0ms delay on first retry.&lt;/li&gt;
&lt;li&gt;Regular PING health checks, and defer to re-init subscriber on ping failure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Patterns beyond cache:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RedisJSON + RediSearch = document DB + search for job/execution browsing &amp;amp; scoped analytics.&lt;/li&gt;
&lt;li&gt;RedisTimeSeries = metrics store suitable for charts and time-windowed KPIs.&lt;/li&gt;
&lt;li&gt;Pub/Sub = real-time event bus for dashboard/SDK triggers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data model &amp;amp; queries (sketch)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Job JSON (id, name, schedule, runMode, userId, org/app, tags, timestamps)&lt;/li&gt;
&lt;li&gt;Execution JSON (id, jobId TAG, status, started/finished, duration, exitCode, stdout, stderr, snippet/language, versions)&lt;/li&gt;
&lt;li&gt;Search examples:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FT.SEARCH idx:executions "@jobId:{&amp;lt;id&amp;gt;|&amp;lt;id2&amp;gt;}" SORTBY startedAt DESC LIMIT 0 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;User-scoped analytics derived by intersecting a user’s job IDs and time window.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scheduling &amp;amp; triggers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A small server-side scheduler scans enabled jobs every 30s, parses minute-level cron, and emits &lt;code&gt;job:trigger&lt;/code&gt; events (per minute de-dupe via a Redis key). For one-time jobs, we either respect &lt;code&gt;runAt&lt;/code&gt; (single fire) or fire on the first cron match.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;Team: Solo submission (credit: &lt;a class="mentioned-user" href="https://dev.to/desmondsanctity"&gt;@desmondsanctity&lt;/a&gt; )&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading and for organizing the challenge!&lt;/p&gt;

</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Copilot vs. Cody: All you need to know</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Wed, 11 Dec 2024 21:23:50 +0000</pubDate>
      <link>https://forem.com/hackmamba/copilot-vs-cody-all-you-need-to-know-jdi</link>
      <guid>https://forem.com/hackmamba/copilot-vs-cody-all-you-need-to-know-jdi</guid>
      <description>&lt;p&gt;The advancement of &lt;strong&gt;artificial intelligence (AI)&lt;/strong&gt; has impacted the software industry, especially with the introduction of AI coding assistants. A &lt;a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/unleashing-developer-productivity-with-generative-ai#/" rel="noopener noreferrer"&gt;McKinsey study&lt;/a&gt; showed that developers using AI tools perform 20%-50% faster on average than those not using it. There has also been a &lt;a href="https://blog.codacy.com/ai-assisted-coding-7-pros-and-cons-to-consider" rel="noopener noreferrer"&gt;debate&lt;/a&gt; about whether these AI coding assistant tools can truly increase or decrease developer's performance, function, productivity ratio, and overall experience.&lt;/p&gt;

&lt;p&gt;As a programmer, I want to be productive and accomplish tasks faster with the help of these AI tools. I also want to use the one that provides the overall best experience regarding integrations, how secure it is, if the code suggestions are of great quality, and how easily I can extend its features and capabilities. This article will look at two of the most used AI coding tools from a developer's point of view.&lt;/p&gt;

&lt;p&gt;While this article isn’t part of this debate, it will highlight the core features, strengths, weaknesses, benchmarks, etc., of these widely used AI coding assistant tools—&lt;a href="https://sourcegraph.com/cody" rel="noopener noreferrer"&gt;Sourcegraph Cody&lt;/a&gt; and &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;—to lead us to an objective conclusion on which is more reliable. Let’s get into the breakdown and comparison of these tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Copilot vs. Cody
&lt;/h2&gt;

&lt;p&gt;In this section, you’ll learn the background of both tools, highlighting their core features and functionalities to help you understand them and their uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is GitHub Copilot?&lt;/strong&gt;&lt;br&gt;
GitHub Copilot was officially launched in June 2021, resulting from a collaboration between GitHub and OpenAI. Powered by OpenAI’s Codex model, Copilot was designed to help developers write code faster by providing real-time suggestions and auto-completions. &lt;/p&gt;

&lt;p&gt;It was released as a plugin on the JetBrains marketplace on October 29, 2021. On June 21, 2022, GitHub announced that Copilot was out of "&lt;strong&gt;technical preview&lt;/strong&gt;" and is available as a &lt;a href="https://github.blog/news-insights/product-news/github-copilot-is-generally-available-to-all-developers/" rel="noopener noreferrer"&gt;subscription-based service&lt;/a&gt; for individual developers after one year of beta testing from developers who got early access.&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%2Fanmc0xf024sdofx3lwp4.jpg" 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%2Fanmc0xf024sdofx3lwp4.jpg" alt="GitHub Copilot · Your AI pair programmer" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core features of Copilot&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot" rel="noopener noreferrer"&gt;&lt;strong&gt;Real-time code suggestions&lt;/strong&gt;&lt;/a&gt;: Copilot offers contextual code suggestions as developers type, including entire functions and complex code snippets.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/asking-github-copilot-questions-in-your-ide" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub Copilot chat&lt;/strong&gt;&lt;/a&gt;: Copilot now offers a chat interface where you can run prompts and generate things like unit tests, code snippets, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-language support&lt;/strong&gt;: It supports many programming languages, from popular ones like Python, JavaScript, and TypeScript to less common languages.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot" rel="noopener noreferrer"&gt;&lt;strong&gt;Natural language to code&lt;/strong&gt;&lt;/a&gt;: Copilot can convert natural language comments into actual code, which is particularly useful for prototyping and quick development tasks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/finding-public-code-that-matches-github-copilot-suggestions" rel="noopener noreferrer"&gt;&lt;strong&gt;Learning from open source&lt;/strong&gt;&lt;/a&gt;: The AI-powered tool is trained on billions of lines of code from public repositories, making it adept at generating relevant code snippets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What is Sourcegraph Cody?&lt;/strong&gt;&lt;br&gt;
Cody, introduced by &lt;a href="https://sourcegraph.com/" rel="noopener noreferrer"&gt;Sourcegraph&lt;/a&gt;, is an AI-powered coding assistant designed to use advanced search and codebase context to help you understand, write, and fix code faster. Launched in 2023, Cody aims to provide deeper context and more accurate code suggestions, particularly for complex and large-scale projects. &lt;/p&gt;

&lt;p&gt;Unlike Copilot, which is heavily geared toward individual developers, Cody is built with a focus on team collaboration and enterprise-level projects.&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%2Fyzny20krwlhq5fmmpv49.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%2Fyzny20krwlhq5fmmpv49.png" alt="Cody is an AI code assistant that uses advanced search and codebase context to help you write and fix code." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core features of Cody&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://sourcegraph.com/docs/cody/core-concepts/context" rel="noopener noreferrer"&gt;&lt;strong&gt;Contextual code intelligence&lt;/strong&gt;&lt;/a&gt;: Cody is designed to understand the broader context of the codebase, making it more effective than many AI coding assistants for large projects with interconnected code files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-file awareness&lt;/strong&gt;: Cody can analyze and provide suggestions based on multiple files within a project, ensuring its suggestions align with the entire codebase's structure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcegraph.com/docs/cody/core-concepts/keyword-search" rel="noopener noreferrer"&gt;&lt;strong&gt;Code search integration&lt;/strong&gt;&lt;/a&gt;: Cody integrates with Sourcegraph's powerful code search capabilities, allowing you to quickly navigate and find relevant code snippets within their projects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcegraph.com/docs/cody/capabilities/commands" rel="noopener noreferrer"&gt;&lt;strong&gt;Custom commands&lt;/strong&gt;&lt;/a&gt;: Cody has an option for creating custom commands that you can use to write &lt;strong&gt;large language model (LLM)&lt;/strong&gt;- powered instructions. These instructions automate boring or repetitive tasks in their day-to-day work, such as documentation, comment writing, etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcegraph.com/docs/cody/capabilities/supported-models" rel="noopener noreferrer"&gt;&lt;strong&gt;Multi LLM support&lt;/strong&gt;&lt;/a&gt;: Cody is designed to use different LLMs for code generation, error fixing, optimization, etc. Cody also allows developers to add their LLM keys, and the usage will be on their account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-language support:&lt;/strong&gt; Cody supports many programming languages, from the popular ones like Python, Javascript, Rust, C, and C++ to the less common ones like Julia or Zig. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sourcegraph.com/enterprise" rel="noopener noreferrer"&gt;&lt;strong&gt;Enterprise-grade security&lt;/strong&gt;&lt;/a&gt;: Cody is tailored for enterprise environments, with enhanced security and privacy features, making it suitable for organizations with strict data protection requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we’ve explored the histories and the core features of the two AI assistant tools, let’s compare them in terms of functions, integrations, security, and developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison between Sourcegraph Cody and GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;This comparison will evaluate how Sourcegraph Cody and GitHub Copilot perform against the chosen criteria, reflecting the expectations developers have when using these tools. The table below will provide an overview, helping to determine which tool stands out as the best option.&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%2Fx23rw41ymq9yeb3of5vn.jpeg" 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%2Fx23rw41ymq9yeb3of5vn.jpeg" alt="Table comparison between Cody and Copilot" width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost comparison&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody is also available on a subscription basis, with pricing significantly better than its counterpart, Copilot. It also has a forever free tier, unlike the 30-day free trial from Copilot, allowing individual developers to have a thorough experience before purchasing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot is available on a subscription basis, with individual and team plans. The pricing is generally affordable for individual developers, but the costs can add up for larger teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ease of setup and use&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Setting up Cody requires developers to &lt;a href="https://sourcegraph.com/docs/cody/clients" rel="noopener noreferrer"&gt;install the extension&lt;/a&gt; from the supported IDEs marketplace, log in with their Sourcegraph account, and start using the tool. Cody can also be accessed on the &lt;a href="https://sourcegraph.com/cody/chat" rel="noopener noreferrer"&gt;web&lt;/a&gt;. The user interface is clean and integrates seamlessly with supported IDEs, providing an additional chat window interface and shortcut keys for handy commands. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Setting up Copilot is relatively straightforward, especially for developers already using Visual Studio Code or JetBrains IDEs. The process involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing the Copilot extension from the marketplace.&lt;/li&gt;
&lt;li&gt;Logging in with a GitHub account.&lt;/li&gt;
&lt;li&gt;Enabling the tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once set up, Copilot provides real-time suggestions as the developer writes code. The user interface is intuitive, with suggestions appearing as semi-transparent text within the IDE, allowing developers to accept, modify, or reject them easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supported IDEs and environment&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody is designed to work best with IDEs that integrate with Sourcegraph’s ecosystem. It supports popular IDEs like the VS Code editor, some JetBrains IDEs like IntelliJ IDEA, PyCharm, etc., and Neovim, which is still in the experimental phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot is primarily designed for Visual Studio Code editor and Visual Studio IDE, but it also supports others like Neovim and JetBrains IDEs like IntelliJ IDEA, PyCharm, etc. This wide IDE support makes it accessible to a broad range of developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compatibility with programming languages&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody supports a wide range of programming languages, including JavaScript, TypeScript, PHP, Python, Java, C/C++, C#, Ruby, Go, SQL, Swift, Objective-C, Perl, Rust, Kotlin, Dart, etc., and shell scripting languages (like Bash, PowerShell).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot boasts compatibility with various programming languages, from mainstream languages like Python, JavaScript, and Ruby to more niche languages. This versatility makes it a go-to tool for developers working across different projects and languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM Integrations&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody defaults to the Claude 3 model from &lt;a href="https://www.anthropic.com/" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt; for its code generation, autocomplete, and chat features, but its strengths lie in its diverse LLM integration(&lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude&lt;/a&gt;, &lt;a href="https://mistral.ai/" rel="noopener noreferrer"&gt;Mistral&lt;/a&gt;, &lt;a href="https://openai.com/chatgpt/" rel="noopener noreferrer"&gt;GPT&lt;/a&gt;, and &lt;a href="https://deepmind.google/technologies/gemini/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt;), unlike Copilot, which solely relies on the GPT models. It also allows users to bring their own API key for the supported LLMs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot stems from a joint effort between GitHub and OpenAI, so Copilot is limited to only the GPT models for its code generation, chat, and autocomplete feature. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quality of code suggestions&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody shines in providing high-quality code suggestions that consider the broader context of the project. Its multi-file awareness and deep codebase understanding enable it to offer more precise and contextually appropriate suggestions, particularly in large projects. It also works well with popular code hosting platforms like GitLab, Bitbucket, etc., with respect to code generation and context awareness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot’s suggestions are generally accurate and contextually relevant, especially for common coding patterns and standard libraries. However, its effectiveness can diminish in more complex scenarios or when dealing with highly customized codebases or codebases whose host is not GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context understanding and accuracy&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody’s ability to analyze and understand the entire codebase gives it an edge in providing accurate suggestions that align with the project’s overall structure. This makes Cody particularly valuable in enterprise environments where code complexity and scale are significant considerations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: While Copilot is good at understanding the immediate context, the difference here is that its suggestions can sometimes lack the depth needed for complex codebases, particularly when the code spans multiple files or involves intricate dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed and efficiency improvements&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: While Cody may not generate suggestions as instantaneously as Copilot, its focus on accuracy and context often results in fewer errors and less time spent correcting code. This can translate into significant efficiency gains for developers working on large, complex projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot is designed to boost developer productivity by providing quick suggestions and auto-completions. This is particularly beneficial in rapid development environments. One of its strengths is its speed in generating suggestions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data handling policies&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody is built with enterprise users in mind and, as such, places a strong emphasis on security and privacy. Sourcegraph offers more control over how data is handled, including options for on-premises deployment, which can alleviate concerns about data exposure in cloud environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: Copilot has faced scrutiny regarding its data handling practices, particularly concerning the training data derived from public repositories. While GitHub has implemented measures to protect user privacy, concerns remain about how user data is used and whether Copilot’s suggestions might inadvertently reproduce copyrighted code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intellectual Property(IP) concerns and business implications&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody’s approach to code suggestions, which focuses on understanding the developer’s existing codebase rather than drawing from a broad corpus of public code, reduces the risk of IP issues. This makes Cody a safer option for businesses concerned about the legal implications of AI-generated code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: The potential for Copilot to suggest code that closely mirrors existing open-source code has raised IP concerns. Developers and organizations need to be cautious about the legal implications of using code suggestions that might be derived from copyrighted material.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Value for money&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Cody&lt;/strong&gt;: Cody offers better value for money because it supports free users with a good feature list. Paid users even get more value and access to a wider range of features like flexible LLM choice, tight security, and context filters, which prevent LLMs from reading sensitive files in the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: For individual developers or small teams working on diverse projects, Copilot offers great value for money, especially given its fast autocomplete, broad language support, and ease of use.&lt;/p&gt;

&lt;p&gt;From comparing features, functions, and differences, Cody emerges at least in my opinion, the clear favorite and a better option. The next section outlines how we reached this conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sourcegraph Cody's strengths over GitHub Copilot
&lt;/h2&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%2Fkc0iw5sqoo7l1x4of8tg.jpeg" 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%2Fkc0iw5sqoo7l1x4of8tg.jpeg" alt="Cody’s core strengths over Copilot" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The comparison table above considers the differences and similarities between GitHub Copilot and Sourcegraph Cody. Undoubtedly, Cody shows to be a better tool when we examine all the criteria. Here are some of the strengths of Cody over Copilot that are worthy of highlighting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://sourcegraph.com/docs/cody/capabilities/autocomplete" rel="noopener noreferrer"&gt;&lt;strong&gt;Superior code suggestions&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; Cody’s ability to use &lt;a href="https://sourcegraph.com/code-search" rel="noopener noreferrer"&gt;Code Search,&lt;/a&gt; another Sourcegraph tool under the hood, allows it to provide more accurate and contextually relevant code suggestions. It also has a feature for &lt;a href="https://sourcegraph.com/docs/cody/capabilities/commands" rel="noopener noreferrer"&gt;prompts and custom commands&lt;/a&gt; where you can tailor it to a particular prompt and reuse it in the codebase for code suggestions or related tasks.&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%2Fomjksjranvq0y7j8ayxg.gif" 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%2Fomjksjranvq0y7j8ayxg.gif" alt="Cody’s custom commands" width="600" height="344"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://sourcegraph.com/docs/cody/capabilities/ignore-context" rel="noopener noreferrer"&gt;&lt;strong&gt;Better context understanding&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; Cody’s multi-file awareness gives it a significant advantage in understanding the broader context of the project. It scans through open files in the editor and files in the same folder and can link a file anywhere in the codebase when using the chat feature.&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%2Fsourcegraph.com%2Fassets%2Fcody%2Fcody-chat-interface-light-v2.svg" 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%2Fsourcegraph.com%2Fassets%2Fcody%2Fcody-chat-interface-light-v2.svg" alt="Cody chat interface to showcase context" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced security and privacy:&lt;/strong&gt; Cody’s focus on &lt;a href="https://sourcegraph.com/security-exhibit.pdf" rel="noopener noreferrer"&gt;enterprise-grade security and privacy&lt;/a&gt; makes it a superior choice for organizations with strict data protection requirements. Admins on the Sourcegraph Enterprise instance can use the &lt;a href="https://sourcegraph.com/docs/cody/capabilities/ignore-context#cody-context-filters" rel="noopener noreferrer"&gt;Cody Context Filters&lt;/a&gt; to determine which repositories Cody can use as the context in its requests to third-party LLMs. Other users can also use the &lt;a href="https://sourcegraph.com/docs/cody/capabilities/ignore-context#cody-ignore-files" rel="noopener noreferrer"&gt;Cody Ignore File&lt;/a&gt;, an experimental feature yet to set files they want Cody to ignore when picking context in the codebase.&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%2Fdi53njqb246r9ltm90e3.gif" 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%2Fdi53njqb246r9ltm90e3.gif" alt="Cody Context Filters feature at work" width="644" height="478"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://sourcegraph.com/terms/cody-notice" rel="noopener noreferrer"&gt;&lt;strong&gt;Data handling and IP protection&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; Cody generates its suggestions by relying on the existing codebase and general LLM knowledge. It does not scan or use open-source codes or other people’s codes. This is a significant advantage for enterprises that must ensure compliance with IP laws and avoid potential legal disputes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost-effectiveness:&lt;/strong&gt; While Cody’s &lt;a href="https://sourcegraph.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt; is fairly lower than Copilot, it offers substantial value for large teams and enterprises and that is a good advantage for the tool.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Addressing common questions and concerns about Copilot
&lt;/h2&gt;

&lt;p&gt;Users have asked these common questions, to know which is better for them between Copilot and other AI coding tools. This section will answer some of these questions from a user perspective to help you understand if Copilot is worth the bargain for your development workflow. Some of these questions are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Copilot worth buying?&lt;/strong&gt;&lt;br&gt;
Copilot is worth considering for individual developers or small teams looking for an affordable AI-assisted coding tool. It provides quick suggestions and supports various programming languages, making it a versatile tool for various projects. However, its effectiveness can vary depending on the complexity of the codebase and the developer’s specific needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the disadvantages of Copilot?&lt;/strong&gt;&lt;br&gt;
The main disadvantages of Copilot include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/copilot/responsible-use-of-github-copilot-features/responsible-use-of-github-copilot-chat-in-your-ide#limitations-of-github-copilot-chat-2" rel="noopener noreferrer"&gt;&lt;strong&gt;Limited context awareness&lt;/strong&gt;&lt;/a&gt;: Copilot’s suggestions are primarily based on the immediate written code, which can lead to less accurate suggestions in complex projects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://githubcopilotlitigation.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;IP concerns&lt;/strong&gt;&lt;/a&gt;: The potential for Copilot to suggest code derived from open-source repositories raises legal and ethical concerns, particularly for businesses.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.techtarget.com/searchsecurity/news/366571117/GitHub-Copilot-replicating-vulnerabilities-insecure-code#:~:text=%22Put%20simply%2C%20when%20Copilot%20suggests,a%20range%20of%20security%20vulnerabilities.%22" rel="noopener noreferrer"&gt;&lt;strong&gt;Security and privacy&lt;/strong&gt;&lt;/a&gt;: As a cloud-based tool, Copilot may not meet the security requirements of all organizations, especially those handling sensitive data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Is it OK to use&lt;/strong&gt; &lt;strong&gt;Copilot&lt;/strong&gt;&lt;strong&gt;?&lt;/strong&gt;&lt;br&gt;
Copilot is generally safe to use for individual developers and small teams, provided they know the potential IP risks and limitations in context understanding. However, organizations should carefully consider these factors, especially if they work with proprietary or sensitive code.&lt;/p&gt;

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

&lt;p&gt;Both Cody and Copilot are powerful AI-assisted coding tools, each with pros and cons. Copilot is an excellent choice for individual developers and small teams looking for an affordable, easy-to-use tool with broad language support, while Cody goes above and beyond in context flexibility, better security and data compliance, access to different LLMs, etc. &lt;/p&gt;

&lt;p&gt;I have explored both GitHub Copilot and Sourcegraph Cody and have seen that each tool has its strengths. After diving into both, I found Cody’s deeper context understanding, access to different LLMs, and a strong focus on security to be game-changers for projects of any size. If you’re curious, I’d recommend &lt;a href="https://sourcegraph.com/cody" rel="noopener noreferrer"&gt;signing up for Cody&lt;/a&gt; and seeing how it fits into your workflow. However, if Copilot aligns better with your needs, it’s also a solid option. Both tools offer valuable features, so exploring what works best for you is worth exploring.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Athene AI - Voice Journaling Assistant</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 24 Nov 2024 16:00:17 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/athene-ai-voice-journaling-assistant-jml</link>
      <guid>https://forem.com/desmondsanctity/athene-ai-voice-journaling-assistant-jml</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/assemblyai"&gt;AssemblyAI Challenge &lt;/a&gt;: No More Monkey Business.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The project Athene AI is a digital journal note powered by AssemblyAI's speech-to-text model. It allows user to record journal entries and it auto transcribes it, generate summaries, proper speech segmentation, does mood analysis and auto create tags for characterizing the note.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The app is live and can be used here:&lt;br&gt;
&lt;a href="https://athene-ai.vercel.app" rel="noopener noreferrer"&gt;Athene AI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code is also available here:&lt;br&gt;
&lt;a href="https://github.com/DesmondSanctity/journal-ai-frontend" rel="noopener noreferrer"&gt;Frontend&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/DesmondSanctity/journal-ai-backend" rel="noopener noreferrer"&gt;Backend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Video Demo&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/YT9AyYc44IU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I figured that I would journal more if i can just talk to something and it gets transcribed to readable words, organized by tags and also have a bit of categorization that will help me remember what the mood or environment was when I entered that journal.&lt;/p&gt;

&lt;p&gt;With LeMUR, when i transcribe the audio of the journal, I used the inbuilt summary method to get summary. I also created two task that is processed by LeMUR: to generate a title for the journal entry and to auto tag it based on categories it fall into.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This submission also qualifies for the Sophisticated Speech-To-Text prompt. Using Universal-2, AssemblyAI’s latest and most accurate speech-to-text model, I built the transcription engine for the journal app. It processes the journal entries smoothly while maintaining a good user experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The whole engine runs on Cloudflare workers deployment with &lt;a href="https://hono.dev" rel="noopener noreferrer"&gt;Hono&lt;/a&gt; web framework. It also uses KV for storage of the transcribed data.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>assemblyaichallenge</category>
      <category>ai</category>
      <category>api</category>
    </item>
    <item>
      <title>Fullstack Serverless Starter with Hono.js, Cloudflare Workers, and Neon Postgres</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sun, 01 Sep 2024 22:50:45 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/fullstack-serverless-starter-with-honojs-cloudflare-workers-and-neon-postgres-2pgj</link>
      <guid>https://forem.com/desmondsanctity/fullstack-serverless-starter-with-honojs-cloudflare-workers-and-neon-postgres-2pgj</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/neon"&gt;Neon Open Source Starter Kit Challenge &lt;/a&gt;: Ultimate Starter Kit&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Kit
&lt;/h2&gt;

&lt;p&gt;This is a starter project for a fullstack serverless application using Hono.js, Cloudflare Workers, and Neon Postgres. It is a simple CRUD application that allows you to create, read, update, and delete posts. It also has provisions for building user interfaces using HTML and CSS.&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%2Fik7fgffd6dizqmlzso9c.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%2Fik7fgffd6dizqmlzso9c.png" alt="starter-preview" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to Kit
&lt;/h2&gt;

&lt;p&gt;This project is available on GitHub. If you find it helpful, star it and share it with others. Contributions are also accepted.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/DesmondSanctity" rel="noopener noreferrer"&gt;
        DesmondSanctity
      &lt;/a&gt; / &lt;a href="https://github.com/DesmondSanctity/neon-serverless-starter" rel="noopener noreferrer"&gt;
        neon-serverless-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is a starter project for a fullstack serverless application using Hono.js, Cloudflare Workers and Neon Postgres. It is a simple CRUD application that allows you to create, read, update and delete posts. It also has provisions for building user interface using HTML and CSS.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Fullstack Serverless Starter with Hono.js, Cloudflare Workers and Neon Postgres.&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This is a starter project for a fullstack serverless application using Hono.js, Cloudflare Workers, and Neon Postgres. It is a simple CRUD application that allows you to create, read, update, and delete posts. It also has provisions for building user interfaces using HTML and CSS.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Prerequisites&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Node.js and npm installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://workers.cloudflare.com/" rel="nofollow noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://neon.tech" rel="nofollow noopener noreferrer"&gt;Neon Postgres&lt;/a&gt; account and database&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Project structure&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;The project structure is as follows:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;---docs
    |-- schemas.ts
---prisma
    |-- schema.prisma
---public
    |-- images.*png,jpg,svg
    |-- favicon.ico
---src
    |-- configs
    |-- controllers
    |-- middlewares
    |-- queries
    |-- routes
    |-- validations
    |-- views
    |-- index.ts
---utils
    |-- constants.js
---tsconfig.json
---wrangler.toml
---package.json
---package-lock.json
---README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The docs folder contains the OpenAPI schemas for generating documentation for the application. It uses &lt;a href="https://hono.dev/examples/zod-openapi" rel="nofollow noopener noreferrer"&gt;@hono/zod-openapi&lt;/a&gt; to generate the schemas. Only the schemas is documented in this starter.&lt;/li&gt;
&lt;li&gt;The prisma folder contains the database schema and migration files.&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/DesmondSanctity/neon-serverless-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Your Journey
&lt;/h2&gt;

&lt;p&gt;Building a fullstack serverless app the conventional way always does not pan out well. You have to make tweaks here and there everytime when you want to build something. To make it easy, I have created this starter project that has most of the things you need for your serverless application, saving you hours of configurations and setups.&lt;/p&gt;

&lt;p&gt;I used Hono.js and Cloudflare workers to create and deploy the APIs, Neon for a serverless database, and integration of Workers AI so to show how easy it is to run AI models in a serverless application. The starter app also uses Twind via SSR to serve Tailwind CSS files, helping us to have a fullstack app with APIs and user interfaces in the same project.&lt;/p&gt;

&lt;p&gt;What is included?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication using JWT.&lt;/li&gt;
&lt;li&gt;Database interaction using &lt;a href="https://www.prisma.io" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Middleware for authentication and logging.&lt;/li&gt;
&lt;li&gt;Validation using &lt;a href="https://zod.dev" rel="noopener noreferrer"&gt;Zod&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Secure Headers.&lt;/li&gt;
&lt;li&gt;Error handling.&lt;/li&gt;
&lt;li&gt;Deployment to Cloudflare.&lt;/li&gt;
&lt;li&gt;Pagination for getAll endpoints.&lt;/li&gt;
&lt;li&gt;Search functionality for getAll endpoints.&lt;/li&gt;
&lt;li&gt;Sorting for getAll endpoints.&lt;/li&gt;
&lt;li&gt;Filtering for getAll endpoints.&lt;/li&gt;
&lt;li&gt;AI Bindings to use &lt;a href="https://developers.cloudflare.com/workers-ai/" rel="noopener noreferrer"&gt;Workers AI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twind.dev" rel="noopener noreferrer"&gt;Twind&lt;/a&gt; configuration that allows use of Tailwind CSS in Cloudflare Workers through SSR.&lt;/li&gt;
&lt;li&gt;Serving of static assets.&lt;/li&gt;
&lt;li&gt;Support for OpenAPI documentation.&lt;/li&gt;
&lt;li&gt;Example user interfaces using HTML and CSS.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>neonchallenge</category>
      <category>postgres</category>
      <category>database</category>
    </item>
    <item>
      <title>AI-powered market analysis for currency pairs with Hono.js, Cloudflare Workers and Twilio</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Sat, 22 Jun 2024 21:17:03 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/ai-powered-market-analysis-for-currency-pairs-with-honojs-cloudflare-workers-and-twilio-5edo</link>
      <guid>https://forem.com/desmondsanctity/ai-powered-market-analysis-for-currency-pairs-with-honojs-cloudflare-workers-and-twilio-5edo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/twilio"&gt;Twilio Challenge v24.06.12&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Nomisma is a cutting-edge AI-driven application that delivers real-time market analysis and updates on currency pairs directly to your WhatsApp. This innovative tool leverages the power of Hono.js, Twilio serverless functions, WhatsApp API, and Cloudflare Workers AI to provide users with timely and insightful financial information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Real-Time Market Analysis: Receive the latest updates on currency pairs, including bid, ask, and mid prices. Detailed analysis to help you make informed decisions on currency trading.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WhatsApp Integration: Get all your updates and analysis sent directly to your WhatsApp for convenient and immediate access. Ensures you are always in the loop, no matter where you are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User-Friendly Interface: Simple and intuitive UI for setting up your preferences and managing notifications. Built using Hono.js for a smooth and responsive user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serverless and Scalable: Utilizes Twilio's serverless functions for efficient and scalable communication handling. Cloudflare Workers AI ensures robust and fast processing of market data and analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Traders: Stay updated with the latest market trends and data for informed trading decisions.&lt;/li&gt;
&lt;li&gt;Financial Analysts: Get regular insights into currency pairs to aid in market research and analysis.&lt;/li&gt;
&lt;li&gt;Investors: Receive timely updates on currency pairs to monitor investments and market movements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/DesmondSanctity" rel="noopener noreferrer"&gt;
        DesmondSanctity
      &lt;/a&gt; / &lt;a href="https://github.com/DesmondSanctity/tyche-nomisma" rel="noopener noreferrer"&gt;
        tyche-nomisma
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An AI tool that you can use to get currency pair update via Whatsapp. Built with Twilio and Cloudflare Workers AI.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;tyche-nomisma&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;An AI tool that you can use to get currency pair update via Whatsapp. Built with &lt;a href="https://hono.dev/" rel="nofollow noopener noreferrer"&gt;Hono.js&lt;/a&gt;, Twilio(serverless functions &amp;amp; whatsapp) and Cloudflare Workers AI.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;prerequisite to run project&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Cloudflare account(with your terminal authenticated with wrangler CLI) &lt;a href="https://developers.cloudflare.com/workers/wrangler/commands/#login" rel="nofollow noopener noreferrer"&gt;Read more here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare API KEY and ACCOUNT ID&lt;/li&gt;
&lt;li&gt;Twilio account(with Twilio WhatsApp sandbox and phone number)&lt;/li&gt;
&lt;li&gt;Tradermade API Key for live rates of currency pairs. &lt;a href="https://tradermade.com/docs/restful-api" rel="nofollow noopener noreferrer"&gt;See more here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;step 1&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;set up a file called &lt;code&gt;.dev.vars&lt;/code&gt; where all your environment variables will be added.&lt;/li&gt;
&lt;li&gt;it will look like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;TRADER_MADE_API = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
CLOUDFLARE_API_KEY = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
CLOUDFLARE_ACCOUNT_ID = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
TWILIO_PHONE_NUMBER = &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;step 2 (run locally)&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;run the following commands to install dependencies and start the dev server respectively.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
npm run dev&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;step 3 (deploy to prod)&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;you should store your secret to cloudflare workers for production use using the command below. where key is…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/DesmondSanctity/tyche-nomisma" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&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%2Fxy01oir0o8ud8mufe8t9.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%2Fxy01oir0o8ud8mufe8t9.png" alt="home-page" width="800" height="400"&gt;&lt;/a&gt;&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%2Fltmvdokb3b6i35g4c3r0.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%2Fltmvdokb3b6i35g4c3r0.png" alt="select-pair" width="800" height="400"&gt;&lt;/a&gt;&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%2Fv5bbmlbh2xdys9nqb7pr.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%2Fv5bbmlbh2xdys9nqb7pr.png" alt="chart-n-scheduler" width="800" height="517"&gt;&lt;/a&gt;&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%2Fc2374z430rgi2u0tofzh.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%2Fc2374z430rgi2u0tofzh.png" alt="whatsapp-message" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the live solution here - &lt;a href="https://tyche-nomisma.0xanon.workers.dev" rel="noopener noreferrer"&gt;Nomisma&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Twilio and AI
&lt;/h2&gt;

&lt;p&gt;How It Works...&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Users sign up and provide their WhatsApp number for receiving notifications.&lt;/li&gt;
&lt;li&gt;Select the currency pairs you are interested in and the intervals at which you want to receive updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Notification and Analysis:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The AI processes market data and generates insightful analysis for the selected currency pairs.&lt;/li&gt;
&lt;li&gt;Notifications are sent to your WhatsApp, ensuring you have accurate information at your fingertips.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technology Stack:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hono.js: Provides a fast, flexible, and scalable web framework for building the application.&lt;/li&gt;
&lt;li&gt;Twilio (&lt;a href="https://www.twilio.com/docs/serverless" rel="noopener noreferrer"&gt;Serverless Functions&lt;/a&gt; &amp;amp; &lt;a href="https://www.twilio.com/docs/whatsapp/quickstart" rel="noopener noreferrer"&gt;WhatsApp API&lt;/a&gt;): Handles the communication and delivery of notifications via WhatsApp.&lt;/li&gt;
&lt;li&gt;Cloudflare Workers AI: Ensures efficient data processing and AI-driven market analysis. I used the &lt;a href="https://developers.cloudflare.com/workers-ai/models/llama-3-8b-instruct/" rel="noopener noreferrer"&gt;@cf/meta/llama-3-8b-instruct&lt;/a&gt; for the analysis generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional Prize Categories
&lt;/h2&gt;

&lt;p&gt;This submission qualifies for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twilio Times Two: I used Twilio serverless function to host the messaging service to make it easily scalable when there are many users. I also used the WhatsApp API for the communication between the app and the users.&lt;/li&gt;
&lt;li&gt;Impactful Innovators: This app is very innovative for getting deep market insight and analysis using AI and APIs that offer historical data. It can be used by traders, investors, and analysts to keep up to date with fundamental, technical, and trend analysis without having to do much work.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>twiliochallenge</category>
      <category>ai</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Faster Postgres Queries with Cloudflare Hyperdrive and Neon</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Wed, 07 Feb 2024 14:36:14 +0000</pubDate>
      <link>https://forem.com/hackmamba/faster-postgres-queries-with-cloudflare-hyperdrive-and-neon-4200</link>
      <guid>https://forem.com/hackmamba/faster-postgres-queries-with-cloudflare-hyperdrive-and-neon-4200</guid>
      <description>&lt;p&gt;If you have a database that gets queried from multiple regions (e.g. via edge functions) and you want to improve overall performance, this guide may help. To demonstrate, we’ll use &lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Neon&lt;/a&gt;, a fully managed serverless PostgreSQL, and &lt;a href="https://developers.cloudflare.com/hyperdrive?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Cloudflare Hyperdrive&lt;/a&gt;, a lightning-fast globally distributed database acceleration platform. When you put Hyperdrive between your database and globally distributed applications and functions that query it, Cloudflare will route and cache queries to reduce latency and improve overall performance.&lt;/p&gt;

&lt;p&gt;This guide will walk you through each step of the process, from setting up Neon Postgres and Cloudflare Hyperdrive to writing custom API services and deploying your application. We'll also cover essential topics such as utilizing Wrangler CLI for Hyperdrive setup, learning how Hyperdrive makes queries faster, and benchmarking native Postgres queries against the Hyperdrive accelerated queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://www.cloudflare.com/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; account.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.cloudflare.com/workers/wrangler?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Wrangler CLI&lt;/a&gt; installed on your local machine.&lt;/li&gt;
&lt;li&gt;Your local machine should have &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js and npm&lt;/a&gt; installed. &lt;a href="https://volta.sh/" rel="noopener noreferrer"&gt;&lt;/a&gt;Wrangler CLI requires a Node version of &lt;code&gt;16.13.0&lt;/code&gt; or later to avoid permission issues.&lt;/li&gt;
&lt;li&gt;A basic knowledge of SQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up Neon Postgres
&lt;/h2&gt;

&lt;p&gt;Setting up your first Neon Postgres project is straightforward—follow the steps below to get started!&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; to sign up for an account. &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%2Fb7jn9wzhrginiki7km3v.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%2Fb7jn9wzhrginiki7km3v.png" alt="Neon Signup Page" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a successful registration, the system will redirect you to create a new project. Here, you can fill in what Postgres version you want to use, the project name, the database name, and the region you want the database instance to run on.&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%2Fdcd069jqsuqocbriun6y.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%2Fdcd069jqsuqocbriun6y.png" alt="Neon Configuration Page" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pop-up with the connection link to your Neon Postgres database will appear when your project is successfully created. Select the &lt;code&gt;psql&lt;/code&gt; option from the dropdown list, copy the string, and save it somewhere. You will use it to connect to Hyperdrive in your demo application later.&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%2Ffjzwj37zi6z2mkal78yd.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%2Ffjzwj37zi6z2mkal78yd.png" alt="Neon Connection Details" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, create a table and insert some data into your database using the Neon SQL Editor. From the side menu, locate the &lt;strong&gt;SQL Editor&lt;/strong&gt; option, click on it, replace the command with the one below, and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Kehinde'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kh@nmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1209867574'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Desmond'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'des@nmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'4584930999'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Franklin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'frk@nmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'3458976048'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Aisha'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'as@nmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0987954890'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Adaeze'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'az@nmial.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1234567890'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This SQL command does three things specifically: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first command, &lt;code&gt;CREATE TABLE users&lt;/code&gt;, creates a table in your database called "users” with columns: &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;phone&lt;/code&gt;. The &lt;code&gt;id&lt;/code&gt; is set to be the primary key and should follow a series, while the other three are text strings and should not be null; that is to say, they shouldn’t be empty when creating a user record.&lt;/li&gt;
&lt;li&gt;The second command, &lt;code&gt;INSERT INTO users&lt;/code&gt;, inserts data into each column of the table as specified in the &lt;code&gt;VALUES&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;The last one, &lt;code&gt;SELECT * FROM users&lt;/code&gt;, returns all the data in the &lt;code&gt;users&lt;/code&gt; table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon successfully running the query above, you’ll get a screen like this one below. This screen is found in the &lt;strong&gt;Tables&lt;/strong&gt; Menu. Click on &lt;strong&gt;Tables&lt;/strong&gt; from the side menu and select &lt;strong&gt;users&lt;/strong&gt; to see the new content created.&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%2F6yqdmt5jduipo697ciqr.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%2F6yqdmt5jduipo697ciqr.png" alt="Users Database Tabel on Neon" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up Cloudflare Hyperdrive and Workers
&lt;/h2&gt;

&lt;p&gt;In this section, you will learn how to set up a Cloudflare Worker project with the Wrangler CLI, bind your Hyperdrive proxied database, and use it in your application. You will be creating the application from scratch.&lt;/p&gt;

&lt;p&gt;If you already have Wrangler CLI installed, you don’t need to worry about this first step. If you don’t have Wrangler CLI installed, run this command in a new terminal to install it globally:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    npm &lt;span class="nb"&gt;install &lt;/span&gt;wrangler@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, navigate to the directory where you want to store your project on your machine and run this command to set up a Cloudflare Workers application.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    npm create cloudflare@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://developers.cloudflare.com/workers?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Workers&lt;/a&gt; is a Cloudflare service that provides an execution environment that allows you to create new applications or augment existing ones without configuring or maintaining infrastructure.&lt;br&gt;
Workers run on Cloudflare’s global network in hundreds of cities worldwide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above command will prompt you to install the &lt;code&gt;create-cloudflare&lt;/code&gt; package and lead you through the setup steps. &lt;/p&gt;

&lt;p&gt;For this guide, set up a basic Workers application by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naming your new Worker directory by specifying where you want to create your application.&lt;/li&gt;
&lt;li&gt;Selecting the &lt;strong&gt;"Hello World"&lt;/strong&gt; script as the type of application you want to create.&lt;/li&gt;
&lt;li&gt;Answering "&lt;strong&gt;yes"&lt;/strong&gt; to using TypeScript.&lt;/li&gt;
&lt;li&gt;You will be asked if you want to deploy the project to Cloudflare. Choose not to deploy and go to the newly created project directory to begin writing code. You will do the deployment later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the setup is done and before creating your Hyperdrive binding, log in with your Cloudflare account for authentication by running the command below:&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;cd&lt;/span&gt; &amp;lt;your-worker-project&amp;gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npx wrangler login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This command will open your browser to log into Cloudflare and acquire access to your terminal. Upon a successful connection, you will get a screen like the one below:&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%2Fu40w8i5qmk7ornivbs3w.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%2Fu40w8i5qmk7ornivbs3w.png" alt="Cloudflare Hyperdrive Connection" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s connect to Hyperdrive now that you have configured Wrangler CLI and have logged in to your Cloudflare account. Run the command below to connect your Neon Postgres to Hyperdrive:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    npx wrangler hyperdrive create pg-hyperdrive &lt;span class="nt"&gt;--connection-string&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-neon-db-connecion-string"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A few things to note in the above command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;wrangler hyperdrive create&lt;/code&gt; command initializes the Hyperdrive creation.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;pg-hyperdrive&lt;/code&gt; is the name of your Hyperdrive. It can be any name of your choice.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--connection-string flag="your-neon-db-connection-string"&lt;/code&gt; is for passing your database connection string as a parameter to the create command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will get a response in your terminal like the one below upon a successful connection.&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%2Fjx0oyfk3n26v5vi9jdks.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%2Fjx0oyfk3n26v5vi9jdks.png" alt="Hyperdrive Successful Setup" width="589" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final step for this section is to set the generated Hyperdrive ID to your &lt;code&gt;wrangler.toml&lt;/code&gt; file, which you will use to connect to Neon Postgres in the next section, as shown below. You must remove the initial content from the package and update your &lt;code&gt;wrangler.toml&lt;/code&gt; file like the one below. Remember to replace the placeholder with your Hyperdrive ID!&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%2F4z5p2dm1wr56y9bs60v5.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%2F4z5p2dm1wr56y9bs60v5.png" alt="wrangler.toml file" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing the API services
&lt;/h2&gt;

&lt;p&gt;If you did everything above, you should have a folder structure like the one below. In this section, you will set up the database connection, create custom query functions, and update the &lt;code&gt;/src/index.ts&lt;/code&gt; file to match your configurations. Also, since you are deploying to Workers, you will use the Hono framework instead of Express. &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%2Fqi65o8mypcsuf21nl7fz.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%2Fqi65o8mypcsuf21nl7fz.png" alt="Project Folder Structure" width="391" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hono is an Ultrafast web framework for Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Lagon, Node.&lt;strong&gt;js&lt;/strong&gt;, and others. You can learn more about Hono &lt;a href="https://hono.dev/?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, install two key packages: the &lt;a href="https://www.npmjs.com/package/pg?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.npmjs.com/package/pg?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Postgres driver&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/pg?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;&lt;/a&gt;and &lt;a href="https://www.npmjs.com/package/hono?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Hono&lt;/a&gt;. Also, you’ll need to install the &lt;a href="https://www.npmjs.com/package/@types/pg?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Postgres driver types&lt;/a&gt; as a dev dependency since you are using Typescript.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    npm i pg hono
    npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @types/pg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create a &lt;code&gt;dbconnect.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; folder and paste the code below.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// src/dbconnect.ts&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HYPERDRIVE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// Connect to your database&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the above code, you set up a database client from the Postgres driver to use the link generated by Hyperdrive as a proxy to your database and return the client instance for your app to use in reading and writing to the database. &lt;/p&gt;

&lt;p&gt;Next, you will write some helper functions that will be used to perform read and write operations to your database through Hyperdrive proxy. You will create a &lt;code&gt;queries.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; directory and paste the code below.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// src/queries.ts&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dbconnect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUsers&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;offset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&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;dbClient&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;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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;result&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;dbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM users LIMIT &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; OFFSET &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUser&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbClient&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;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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;result&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;dbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbClient&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;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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;result&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;dbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`INSERT INTO users (name, email, phone) VALUES ('&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, you are writing three different functions that get all the users, get a single user, and create a user in the database. For each function, you are passing the context parameter, which contains the request body or parameters, to the database client. The database client will instantiate with these parameters. After that, you can use the database client to make your queries or insertion. After a successful query or insertion, it returns the result to be read in the application.&lt;/p&gt;

&lt;p&gt;Finally, update the contents of the &lt;code&gt;index.ts&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; folder to match the code below.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// src/index.ts&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&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;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cors&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;hono/cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prettyJSON&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;hono/pretty-json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createUser&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;./queries&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&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;./dbconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;HYPERDRIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hyperdrive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pretty Users API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;prettyJSON&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Route Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;404&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Bindings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Env&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&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;getUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;In the code above, you obtained the Hyperdrive data from &lt;code&gt;wrangler.toml&lt;/code&gt;. You first created an app instance with Hono and set the basic endpoint that you can use to check your routing works fine and make the JSON response formatted with the &lt;code&gt;prettyJSON()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;After that, you created another instance of Hono called API, which has the Env binding. This is so that you can read the value of the Hyperdrive ID from the context anywhere in the app. Hopefully, it now makes sense why you passed the context to your query functions and database connection function too. &lt;/p&gt;

&lt;p&gt;Finally, you defined each route to call their respective query functions and pass the context down to them. The API route is now defined so that your endpoint calls will be to &lt;code&gt;api/users/…&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, this is a Cloudflare Worker application using Hono, so in the index, you can read the data in your .toml file as a type of Env.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Deploying to Cloudflare Workers
&lt;/h2&gt;

&lt;p&gt;Now that everything is set up, the next step is to deploy your application to Cloudflare Workers, the serverless execution environment. To deploy your application, run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    npx wrangler deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will deploy your application to Workers. If you are not logged in to the Wrangler CLI, the command will prompt you to log in for authentication; your application will then be deployed. You will get a response upon a successful deployment, as shown in the image below.&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%2Ff7hf79v98klg840vpipt.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%2Ff7hf79v98klg840vpipt.png" alt="Deploy to Cloudflare" width="684" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing and benchmarking with vs without Hyperdrive
&lt;/h2&gt;

&lt;p&gt;The core reason for using Hyperdrive to proxy the database instead of using the Neon connection URL directly in your application is the optimization for speed and performance when making queries. This section compares the response time for a large query from across the world, first using our new API on Hyperdrive and then using a traditional single-region API server. As you can see below, the Hyperdrive route is significantly faster.&lt;/p&gt;

&lt;p&gt;The link to the applications used for the demo can be found here on &lt;a href="https://github.com/DesmondSanctity/neon-hyperdrive?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a query to fetch all users in the database for Neon Postgres and Cloudflare Hyperdrive proxy. It responded at 780ms on the first query and between 300ms and 450ms on subsequent queries.&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%2Fmb6z0y90fhu2ricuoxdk.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%2Fmb6z0y90fhu2ricuoxdk.png" alt="API query for Neon Hyperdrive Setup" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a similar query to fetch all users in the database that’s routing through a single API server. It responded at 1888ms at the first query and between 600ms and 3s on subsequent queries.&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%2Feinngjihrrg3opzgd2w2.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%2Feinngjihrrg3opzgd2w2.png" alt="API query for Native Postgres and Sequelize" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This video demonstrates the speed test between Neon Postgres connected through Hyperdrive and Neon Postgres connected through an API App in a single region.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://app.opentape.io/share/3b6d4fae-65d2-4f00-9341-f0cb574476be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.opentape.io%2Fthumbnail%2F3b6d4fae-65d2-4f00-9341-f0cb574476be" height="720" class="m-0" width="1280"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://app.opentape.io/share/3b6d4fae-65d2-4f00-9341-f0cb574476be" rel="noopener noreferrer" class="c-link"&gt;
          Speed Benchmarking For Databases Queries | Opentape
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Desmond Obisi - Feb 7th, 2:21pm
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.opentape.io%2Ffavicon.ico" width="32" height="32"&gt;
        app.opentape.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Why was the Workers + Hyperdrive approach faster?
&lt;/h2&gt;

&lt;p&gt;A few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Workers are deployed globally, close to users&lt;/strong&gt; - for most of the world, the network latency between local device and worker function is lower than when using a single API server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hyperdrive keeps a connection to the database alive -&lt;/strong&gt; Hyperdrive keeps a “warm” connection to the database alive across function calls. This eliminates the multiple round trips usually required to establish a new connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hyperdrive caches certain responses -&lt;/strong&gt; For &lt;code&gt;SELECT&lt;/code&gt; queries, Hyperdrive &lt;a href="https://developers.cloudflare.com/hyperdrive/learning/query-caching/" rel="noopener noreferrer"&gt;caches responses for 60 seconds by default&lt;/a&gt;, so repetitive queries will never even hit the database.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Congratulations! If you followed along, you learned to use &lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Neon&lt;/a&gt; &lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Serverless&lt;/a&gt; &lt;a href="https://neon.tech/?ref=hm" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; with Cloudflare Hyperdrive to achieve faster database queries. You also learned about Workers, Wrangler CLI for interfacing with Cloudflare's products, and Hono for writing Node.js APIs that can be deployed to Workers.&lt;/p&gt;

&lt;p&gt;Other things that can be explored on this subject leveraging Hyperdrive for intensive database operations are transactions and query caching. You can check the reference section for a feel of other things Cloudflare offers with the Hyperdrive developer tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/hyperdrive?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;HyperDrive Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/wrangler?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Wrangler CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hono.dev/getting-started/cloudflare-workers?utm_source=hackmamba&amp;amp;utm_medium=blog&amp;amp;utm_id=HMBcommunity" rel="noopener noreferrer"&gt;Hono.dev Cloudflare Workers Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.logrocket.com/build-web-application-hono/" rel="noopener noreferrer"&gt;Build a Web Application with Hono&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neon.tech/docs/introduction?ref=hm" rel="noopener noreferrer"&gt;Neon Postgres Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>neon</category>
      <category>postgres</category>
      <category>cloudflare</category>
      <category>database</category>
    </item>
    <item>
      <title>Implement OTP Verification using Redis and Node.js</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Tue, 30 May 2023 11:11:38 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/implement-otp-verification-using-redis-and-nodejs-572c</link>
      <guid>https://forem.com/desmondsanctity/implement-otp-verification-using-redis-and-nodejs-572c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In today's digital space, user authentication and verification are crucial components of any application's security measure. One-time password (OTP) systems provide an extra layer of security by generating unique codes that can be used once and are valid for a short period.&lt;/p&gt;

&lt;p&gt;This article will guide you through the process of building an OTP and verification feature using Redis and Node.js. Redis, a powerful in-memory data structure store, will be utilized to store and manage the OTPs efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of OTP-based Authentication
&lt;/h2&gt;

&lt;p&gt;In this section, we will go through an overview of OTP-based authentication and discuss its advantages over traditional password-based systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is OTP-based Authentication?
&lt;/h3&gt;

&lt;p&gt;One-time password (OTP) authentication is a mechanism that provides an additional layer of security during an authentication process. &lt;/p&gt;

&lt;p&gt;Unlike traditional password-based authentication, which relies on fixed passwords, OTP-based authentications generate random and unique codes that are valid for a short period (typically a few minutes). Users are required to provide the OTP along with their username or email to gain access to the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of OTP-based Authentication:
&lt;/h3&gt;

&lt;p&gt;OTP-based authentication offers several advantages over traditional password-based systems, a few of which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased Security&lt;/li&gt;
&lt;li&gt;Mitigation of Password-related Risks&lt;/li&gt;
&lt;li&gt;Two-factor Authentication (2FA)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing OTP-based authentication and verification in your applications, you can significantly enhance the security of user authentication while providing a seamless, user-friendly experience.&lt;/p&gt;

&lt;p&gt;Let's move on to the next section, where we will set up the development environment for our project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Development Environment
&lt;/h2&gt;

&lt;p&gt;In this section, we will go through the process of setting up the development environment for building the OTP and verification feature with Redis and Node.js. We will first cover the installation of Node.js, setting up Redis, and creating a new Node.js project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Node.js:
&lt;/h3&gt;

&lt;p&gt;To begin, you need to install Node.js on your system. Node.js is available for multiple operating systems and can be downloaded from the official Node.js &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;website&lt;/a&gt;. Follow these steps to install Node.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit the Node.js &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;website&lt;/a&gt; using your web browser.&lt;/li&gt;
&lt;li&gt;Download the appropriate Node.js installer for your operating system (Windows, macOS, or Linux).&lt;/li&gt;
&lt;li&gt;Run the installer and follow the on-screen instructions to complete the installation process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify that Node.js is installed correctly, open a command prompt or terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see the version number of Node.js printed in the console.&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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--PyHcy3Rd--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4sljds39hhp7lt0jc5x.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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--PyHcy3Rd--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw4sljds39hhp7lt0jc5x.png" alt="Node Version" width="753" height="81"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up Redis
&lt;/h3&gt;

&lt;p&gt;Next, you need to set up Redis, the in-memory data structure store that will be used to store and manage the OTPs. Follow these steps to install and configure Redis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit the Redis &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;website&lt;/a&gt; using your web browser.&lt;/li&gt;
&lt;li&gt;Download the Redis distribution package for your operating system. Since Redis does not run directly on Windows, you can either run it through &lt;a href="https://redis.io/docs/getting-started/installation/install-redis-on-windows/" rel="noopener noreferrer"&gt;WSL2 (Windows Subsystem for Linux)&lt;/a&gt; or by downloading this port of Redis for Windows &lt;a href="https://github.com/microsoftarchive/redis/releases" rel="noopener noreferrer"&gt;here&lt;/a&gt; and installing it. In this tutorial, we will be using the latter.&lt;/li&gt;
&lt;li&gt;Open a command prompt or terminal and run the Redis server by executing the following command:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    redis-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will start the Redis server on the default port &lt;code&gt;6379&lt;/code&gt;.&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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--dQlAI5pc--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizkr1jbgbz4q0hhzg10h.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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--dQlAI5pc--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizkr1jbgbz4q0hhzg10h.png" alt="Redis CLI" width="800" height="96"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a New Node.js Project
&lt;/h3&gt;

&lt;p&gt;Now that you have Node.js and Redis all setup, let's create a new Node.js project for building the OTP and verification feature.&lt;/p&gt;

&lt;p&gt;Follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a command prompt or terminal and create or navigate to the directory where you want to create your project.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    mkdir tutorials &amp;amp;&amp;amp; cd tutorials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Run the following command to clone the template that I made for this project in my GitHub repository and after that, run it.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    git clone https://github.com/DesmondSanctity/node-redis-otp.git &amp;amp;&amp;amp; npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will give you most of the project setup except the various logic for the OTP generation, Redis caching and data retrieval, which we will be implementing in the next section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generating and Caching OTPs with Redis
&lt;/h2&gt;

&lt;p&gt;In this section, we will explore the implementation details of generating OTPs and caching the data with Redis. We will use a user signup/login system to demonstrate this concept. We would also use a Node.js client library &lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;otp-generator&lt;/code&gt; to interact with the local Redis instance and generate unique OTPs respectively.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connecting to the Local Redis Instance
&lt;/h3&gt;

&lt;p&gt;To utilize the caching capabilities of Redis, set up and connect to the local Redis instance you installed on your machine. In the &lt;code&gt;server.js&lt;/code&gt; file, set up the connection as shown below and export it for use in other files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// create a client connection&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// on the connection&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connected to Redis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating OTP for User and Storing to Redis
&lt;/h3&gt;

&lt;p&gt;Now that you have established a connection with your local Redis installation, you can test generating OTPs and storing them in Redis. In the &lt;code&gt;userController.js&lt;/code&gt; file where the user logic is, add a method to generate OTP and store OTP using the packages we installed earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;export&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;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Generate a random OTP using the otp-generator package&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;otpGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;lowerCaseAlphabets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;upperCaseAlphabets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;specialChars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// check the existing user&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existUsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please use unique username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

                    &lt;span class="nf"&gt;resolve&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;// check for existing email&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please use unique Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

                    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;existUsername&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;existEmail&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hashedPassword&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                    &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hashedPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="nx"&gt;lastName&lt;/span&gt;
                                &lt;span class="p"&gt;});&lt;/span&gt;

                                &lt;span class="c1"&gt;// Store the OTP in Redis, with the user's email as the key&lt;/span&gt;
                                &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;responseUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                &lt;span class="c1"&gt;// return save result as a response&lt;/span&gt;
                                &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                        &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User Register Successfully&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="na"&gt;OTP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseUser&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enable to hashed password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                                &lt;span class="p"&gt;})&lt;/span&gt;
                            &lt;span class="p"&gt;})&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="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="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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above code, the &lt;code&gt;otpGenerator&lt;/code&gt; function from &lt;code&gt;otp-generator&lt;/code&gt; package was used to generate four unique integers for the OTP and to store them in the Redis client using &lt;code&gt;client.set(key, value)&lt;/code&gt;; where &lt;code&gt;value&lt;/code&gt; is the OTP we are storing and &lt;code&gt;key&lt;/code&gt; is a unique identifier we will use to identify each stored value. In this case, the email of the user will be used as the &lt;code&gt;key&lt;/code&gt; because it is unique per user.&lt;/p&gt;

&lt;p&gt;You can check your local instance to see if the OTP is saved by querying Redis with the key/identifier. Run the command below to see the resulting OTP for a user with email &lt;code&gt;example@gmail.com&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;    redis-cli
    GET example@gmail.com 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--W8VssJjx--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg45gigel3aok9zim26xi.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%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--W8VssJjx--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_800%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg45gigel3aok9zim26xi.png" alt="Redis CLI Result" width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying OTP for User
&lt;/h3&gt;

&lt;p&gt;So far, a unique OTP has been generated on user registration, now we need to verify the user by writing another block of code. The logic here is to check the OTP to be supplied to the code block with the one in the local Redis instance, if they match, the user will be verified. If there is no match, the user will be notified that the OTP is incorrect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;export&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;verifyUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// check the user existance&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;exist&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;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;exist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can't find User!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// Retrieve the stored OTP from Redis, using the user's email as the key&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storedOTP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storedOTP&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// If the OTPs match, delete the stored OTP from Redis&lt;/span&gt;
                &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// Update the user's isVerified property in the database&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAndUpdate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

                &lt;span class="c1"&gt;// Send a success response&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OTP verified successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// If the OTPs do not match, send an error response&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid OTP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;next&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the code, the &lt;code&gt;client.get(key)&lt;/code&gt; method was used to get the stored OTP and to compare it with the one input by the user. If there is a match, the OTP will be deleted using &lt;code&gt;client.del(key)&lt;/code&gt; method to avoid any vulnerabilities and the user info &lt;code&gt;isVerified&lt;/code&gt; will be updated to true.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detecting Missed Verification
&lt;/h3&gt;

&lt;p&gt;Sometimes, a user might register successfully but will fail to complete OTP verification. In such a case, the database will not record them as verified users yet, any attempt by such a  user to log in will initiate a response with a notification for them to complete OTP verification. Also, a new code will be generated for them to use. An example code is below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;export&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;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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="nx"&gt;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;bcrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordCheck&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;passwordCheck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Don't have Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                            &lt;span class="p"&gt;});&lt;/span&gt;

                            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="c1"&gt;// Generate a random OTP using the otp-generator package&lt;/span&gt;
                                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;otpGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="na"&gt;lowerCaseAlphabets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="na"&gt;upperCaseAlphabets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="na"&gt;specialChars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
                                &lt;span class="p"&gt;});&lt;/span&gt;

                                &lt;span class="c1"&gt;// Store the OTP in Redis, with the user's email as the key&lt;/span&gt;
                                &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User is not verified, please finish verification using this OTP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="na"&gt;OTP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;otp&lt;/span&gt;
                                &lt;span class="p"&gt;})&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;

                            &lt;span class="c1"&gt;// create jwt token&lt;/span&gt;
                            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                &lt;span class="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login Successful...!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;token&lt;/span&gt;
                            &lt;span class="p"&gt;});&lt;/span&gt;

                        &lt;span class="p"&gt;})&lt;/span&gt;
                        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password does not Match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                        &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username not Found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code block above, after the login credentials has been checked and ascertained correct, the &lt;code&gt;isVerified&lt;/code&gt; parameter will also be checked to see if the user is verified. If they are not verified, a new OTP is generated for them with a notification to complete the verification using the &lt;code&gt;verify&lt;/code&gt; endpoint that was done initially. Once they are verified, they can successfully log in to the system.&lt;/p&gt;

&lt;p&gt;This is a short video demonstrating how the whole code put together works.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/irBK4pgCu6I"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;Implementing OTP-based authentication and verification is an effective way to strengthen the security of your applications. By using Redis as a data store and Node.js as the backend, you can easily build a robust OTP system.&lt;/p&gt;

&lt;p&gt;Throughout this article, we went through step-by-step instructions and code examples to guide you in building this feature. With the knowledge gained from this tutorial, you are now equipped with adequate knowledge to implement OTP-based authentication and verification in your own projects, ensuring a safer user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoftarchive/redis" rel="noopener noreferrer"&gt;A Window Port for Redis Installation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/docs/" rel="noopener noreferrer"&gt;Redis Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codedaddy.hashnode.dev/introduction-to-caching-using-redis" rel="noopener noreferrer"&gt;Introduction to Caching Using Redis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>redis</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Setting up Node.js Email Server with Nodemailer and Mailtrap</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Wed, 22 Mar 2023 01:15:27 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/setting-up-nodejs-email-server-with-nodemailer-and-mailtrap-404</link>
      <guid>https://forem.com/desmondsanctity/setting-up-nodejs-email-server-with-nodemailer-and-mailtrap-404</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The email service is one of the services common to every software application. Emails are used to communicate pieces of information mostly about downtime, updates and verification of activities by the user in a software application. &lt;br&gt;
In this tutorial, we will be demonstrating how to set up an email server in a Node.js application using Nodemailer and Mailtrap. First of all, let us learn the basics!&lt;/p&gt;

&lt;h3&gt;
  
  
  Nodemailer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nodemailer.com/about/" rel="noopener noreferrer"&gt;&lt;strong&gt;Nodemailer&lt;/strong&gt;&lt;/a&gt; is a module for Node.js applications that allows easy-as-cake email sending. The project started back in 2010 when there was no better option for sending email messages, today it is still the solution most Node.js users turn to by default. Underneath the hood, Nodemailer uses the Node.js core module &lt;code&gt;net&lt;/code&gt; to create a connection to the SMTP server specified in the transport object. Once a connection is established, Nodemailer uses the SMTP protocol to send email messages by sending various commands and responses over the connection. &lt;/p&gt;

&lt;p&gt;Here's a simplified example of how Nodemailer sends an email message:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a transport object that defines the SMTP server, authentication credentials, and other options.&lt;/li&gt;
&lt;li&gt;Create a message object that defines the email message, including the sender, recipient, subject, and body.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;transporter.sendMail()&lt;/code&gt; method to send the message, passing in the message object and a callback function to handle any errors or responses.&lt;/li&gt;
&lt;li&gt;Nodemailer uses the transport object to establish a connection to the SMTP server and send the message using the SMTP protocol. If the message is sent successfully, Nodemailer returns a response object containing information about the message, like the message ID and the response from the SMTP server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, Nodemailer provides a simple and powerful interface for sending email messages in Node.js, taking away the complexities of the SMTP protocol and allowing developers to focus on the content and delivery of their messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mailtrap
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mailtrap.io/home" rel="noopener noreferrer"&gt;&lt;strong&gt;Mailtrap&lt;/strong&gt;&lt;/a&gt; is a service that provides a mock SMTP server for testing and development purposes. It allows developers to test their email-sending functionality without actually sending emails to real recipients. Mailtrap works by creating a temporary SMTP server that is accessible through a unique hostname and port number. When an email is sent to the Mailtrap server, it is intercepted and stored in your Mailtrap inbox instead of being sent to the intended recipient. Mailtrap also provides a web interface that allows you to view and manage your test email messages, as well as features like email forwarding and spam testing.&lt;/p&gt;

&lt;p&gt;Here are the following steps to enable Mailtrap to work with Nodemailer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign up for a Mailtrap account and create a new inbox.&lt;/li&gt;
&lt;li&gt;Retrieve your SMTP credentials from the Mailtrap inbox settings.&lt;/li&gt;
&lt;li&gt;Configure Nodemailer to use the Mailtrap SMTP server by creating a transport object with the SMTP settings from Mailtrap.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;transporter.sendMail()&lt;/code&gt; method to send email messages, just like you would with any other SMTP server.&lt;/li&gt;
&lt;li&gt;Check your Mailtrap inbox to see the test email message. The message will be displayed in the inbox with its headers and content, allowing you to verify that it was sent correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, Mailtrap is a useful tool for testing and debugging email functionality in a safe and controlled environment. By using Mailtrap with Nodemailer, developers can test their email-sending functionality without the risk of accidentally sending test emails to real recipients or getting blocked by spam filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the Email Server
&lt;/h2&gt;

&lt;p&gt;Now that we’ve had the basics out of the way, let us delve into a more interesting part: we will be setting up the email server using Node.js. The process follows this form;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Mailtrap Account
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to the Mailtrap website and sign up. When the sign up is completed, you will see a screen like the one below.&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%2F4r0o0e712qzr3a1z96tp.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%2F4r0o0e712qzr3a1z96tp.png" alt="mailtrap-setup1" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on Email Testing to get your secret credentials. Select Nodemailer as an integration option and you will get a screen like the one below.&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%2Fd93tmp0v3favtfrh33td.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%2Fd93tmp0v3favtfrh33td.png" alt="mailtrap-setup2" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point, you’d have to save the keys, it would be needed to set up the application later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup Node.js Project
&lt;/h2&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To set up our Node.js server, we need to create a project folder that will contain our application. For this tutorial, we will call the folder &lt;code&gt;node-email-server&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To begin, we will run the following commands from our terminal.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;desktop&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
    &lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
    &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;    &lt;span class="cm"&gt;/** this is to open the folder in our code editor (VSCode) */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In the root folder, run the following command to install the packages we will be using
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;nodemon&lt;/span&gt; &lt;span class="nx"&gt;nodemailer&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="nx"&gt;mailgen&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nodemon—automatically restarts the node application when file changes in the directory are detected.&lt;br&gt;
Cors—for providing an Express middleware that can be used to enable &lt;a href="http://en.wikipedia.org/wiki/Cross-origin_resource_sharing" rel="noopener noreferrer"&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/a&gt; with various options.&lt;br&gt;
Express—Node.js web application framework that provides a robust set of features for web and mobile applications.&lt;br&gt;
Mailgen—Node.js package that generates clean, responsive HTML e-mails for sending transactional mail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the root folder—this is where we will store the Mailtrap keys we generated earlier.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s email address&amp;gt;
    USER=&amp;lt;mailtrap user key&amp;gt;
    PASSWORD=&amp;lt;mailtrap password&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We will proceed to create the server file, in our case, it is the &lt;code&gt;index.js&lt;/code&gt; file. Add the following code to the server file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&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;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;nodemailer&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;nodemailer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Mailgen&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;mailgen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;// configure our app to use env variables&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="cm"&gt;/** middlewares */&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-powered-by&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// less hackers know about our stack&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/** HTTP GET Request */&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Health Check PASS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;


    &lt;span class="c1"&gt;// create reusable transporter object using the default SMTP transport&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nodemailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smtp.mailtrap.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2525&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// generated mailtrap user&lt;/span&gt;
            &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// generated mailtrap password&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// generate email body using Mailgen&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MailGenerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Mailgen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;product&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://mailgen.js/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c1"&gt;// define a route for sending emails&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/send-email&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// get the recipient's email address, name and message from the request body&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// body of the email&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;intro&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to Test Mail! We&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;re very excited to have you on board.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;outro&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Need help, or have questions? Just reply to this email, we&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;d love to help.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MailGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// send mail with defined transport object&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mailOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;emailBody&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;transporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mailOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error sending email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email sent: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Email sent successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// start the server&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server started on port &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly, we imported the packages we will be needing and created an app instance using &lt;code&gt;express&lt;/code&gt;. We defined our middleware and port. The interesting parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The email transport instance was created with &lt;code&gt;nodemailer&lt;/code&gt; and using the SMTP details that &lt;code&gt;mailtrap&lt;/code&gt; provided us&lt;/li&gt;
&lt;li&gt;The function &lt;code&gt;MailGenerator&lt;/code&gt;  which we used to generate clean, responsive HTML e-mails.&lt;/li&gt;
&lt;li&gt;The route which houses those logics we need for sending email requests to the server whenever the &lt;code&gt;send-email&lt;/code&gt; endpoint is called.&lt;/li&gt;
&lt;li&gt;Finally, we started the server and let the app listen to the defined port which we will use to access and test our email API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing our Email Server
&lt;/h2&gt;

&lt;p&gt;This is my favourite part of the whole process, testing our endpoint to see if our application actually works. For this tutorial, we will be using &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; a HTTP client used to test, debug or document API. You can set up Postman as a desktop application or use it as a web app without installing it on your local machine. Check &lt;a href="https://learning.postman.com/docs/getting-started/installation-and-updates/" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more details on how to set it up with any option you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making a request
&lt;/h3&gt;

&lt;p&gt;Our app is running on &lt;code&gt;http://localhost:8080&lt;/code&gt; so our endpoint is exposed to this url: &lt;code&gt;http://localhost:8080/send-email&lt;/code&gt; We will send a &lt;code&gt;POST&lt;/code&gt; request to the endpoint with three parameters in the body of the request which are: &lt;code&gt;to&lt;/code&gt;, the email address of the recipient of the mail, &lt;code&gt;name&lt;/code&gt;, the name of the recipient and &lt;code&gt;message&lt;/code&gt;, which is optional if we do not want to overwrite the default message in our application. Here is what our Postman request and response will look like.&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%2Frsdkhbg13oki1oiv5ctj.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%2Frsdkhbg13oki1oiv5ctj.png" alt="postman-test" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we check our Mailtrap account, we will see the mail we just sent and viola! our app works as expected. Here is what mine looks like.&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%2Fb0qra1i3h9hxb3paghus.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%2Fb0qra1i3h9hxb3paghus.png" alt="mailtrap-inbox" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We have learnt how to use Nodemailer and Mailtrap to set up a Node.js email server and also how these technologies work behind the scene. The code repository for this project can be found &lt;a href="https://github.com/DesmondSanctity/node-email-server" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Note that Nodemailer works with other SMTP providers like Gmail, Hotmail, MailGun, SendGrid, SendinBlue, etc. The list of supported services can be found &lt;a href="https://nodemailer.com/smtp/well-known/" rel="noopener noreferrer"&gt;here&lt;/a&gt; too. &lt;/p&gt;

&lt;p&gt;If you enjoyed the article, feel free to leave a comment, give it a shout-out and share it with others. Bueno!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>nodemailer</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Documenting Node.js API using Swagger</title>
      <dc:creator>Desmond Obisi</dc:creator>
      <pubDate>Mon, 20 Mar 2023 09:56:43 +0000</pubDate>
      <link>https://forem.com/desmondsanctity/documenting-nodejs-api-using-swagger-4klp</link>
      <guid>https://forem.com/desmondsanctity/documenting-nodejs-api-using-swagger-4klp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;API documentation is an integral part of developing software. It is an instruction manual that explains how to use an API and its services. This manual might contain tutorials, code examples, screenshots, and anything else that helps users better understand how to work with the API. &lt;/p&gt;

&lt;p&gt;In this article, we will be learning how to document API written in Node.js using a tool called Swagger. Swagger allows you to describe the structure of your APIs so that machines can read them. The ability of APIs to describe their own structure is the root of all awesomeness in Swagger. Why is it so great? Well, by reading our API’s structure, swagger can automatically build beautiful and interactive API documentation. It can also automatically generate client libraries for your API in many languages and explore other possibilities like automated testing. Swagger does this by asking our API to return a YAML or JSON that contains a detailed description of your entire API. This file is essentially a resource listing of our API which adheres to &lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md" rel="noopener noreferrer"&gt;OpenAPI Specification&lt;/a&gt;&lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md" rel="noopener noreferrer"&gt;s&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building our API with Node.js and Express
&lt;/h2&gt;

&lt;p&gt;To start writing the API specs, we will build our API with Node.js which is a back-end JavaScript runtime environment that runs on the V8 JavaScript Engine and executes JavaScript code outside a web browser. For the purpose of simplicity, I have set up the project and it can be cloned from this &lt;a href="https://github.com/DesmondSanctity/node-js-swagger" rel="noopener noreferrer"&gt;GitH&lt;/a&gt;&lt;a href="https://github.com/DesmondSanctity/node-js-swagger" rel="noopener noreferrer"&gt;ub repository&lt;/a&gt;.  To get the backend running on our local machine, we will follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new folder for the project and run this command in the root folder to clone the repository
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/DesmondSanctity/node-js-swagger.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To successfully run the code, we will need a database connection. I used a MongoDB Atlas cluster for the database and we can follow this &lt;a href="https://www.mongodb.com/basics/mongodb-atlas-tutorial" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; to set up one, it is quite simple to set up. After the set up, we will get our URL and that is all we need to connect to our database from our app.&lt;/li&gt;
&lt;li&gt;We are using JSON Web Token (JWT) to authenticate access to our API, so we will generate a secret key that will be used by our app to send and verify requests. To generate that, we will run this command anywhere in our terminal. This script will generate a random 64-bit ASCII string, that can be used for encrypting JWT tokens.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We will now create a file named &lt;code&gt;.env&lt;/code&gt; where we will store our MongoDB Atlas cluster URL and JWT secret as an environment variable. The file should look like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    JWT_SECRET=&amp;lt;your JWT secret&amp;gt;
    ATLAS_URI=&amp;lt;your MongoDB Atlas cluster URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now, we are ready to run the application but first, we will install some packages and then start the application. If you cloned the &lt;a href="https://github.com/DesmondSanctity/node-js-swagger" rel="noopener noreferrer"&gt;GitH&lt;/a&gt;&lt;a href="https://github.com/DesmondSanctity/node-js-swagger" rel="noopener noreferrer"&gt;ub repository&lt;/a&gt; earlier, you just need to run these commands below:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    npm install   // To install the neccessary packages
    npm start    //  To start the application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you are successful at this point, you will see the following message in your terminal
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mini&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;nodemon&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nodemon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nodemon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;restart&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;enter&lt;/span&gt; &lt;span class="s2"&gt;`rs`&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nodemon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;watching&lt;/span&gt; &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nodemon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;watching&lt;/span&gt; &lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;mjs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;nodemon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;starting&lt;/span&gt; &lt;span class="s2"&gt;`node server.js`&lt;/span&gt;
    &lt;span class="nx"&gt;Database&lt;/span&gt; &lt;span class="nx"&gt;Connected&lt;/span&gt;
    &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="nx"&gt;connected&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding Swagger UI and Configurations
&lt;/h2&gt;

&lt;p&gt;Now that we have our API ready, we will start defining the Swagger specs for them. There are two ways we can build Swagger documentation for our API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually write the specs inside the router files or a dedicated json or yaml file in our application.&lt;/li&gt;
&lt;li&gt;Using existing developer’s tools or packages to generate the documentation automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, we will use the manual approach to ensure accuracy in our definitions and specifications. First, we will install two packages called &lt;code&gt;"swagger-jsdoc"&lt;/code&gt; and &lt;code&gt;"swagger-ui-express"&lt;/code&gt; as dependencies using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install swagger-jsdoc swagger-ui-express --save-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation, we will create a new file called &lt;code&gt;swagger.js&lt;/code&gt; in the root directory of our application and paste the following code into it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;swaggerJsdoc&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;swagger-jsdoc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;swaggerUi&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;swagger-ui-express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mini Blog API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API endpoints for a mini blog services documented on swagger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Desmond Obisi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;info@miniblog.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://github.com/DesmondSanctity/node-js-swagger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8080/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Local server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;your live url here&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Live server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// looks for configuration in specified directories&lt;/span&gt;
      &lt;span class="na"&gt;apis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./router/*.js&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swaggerSpec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;swaggerJsdoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;swaggerDocs&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;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Swagger Page&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swaggerUi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;swaggerUi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swaggerSpec&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;// Documentation in JSON format&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/docs.json&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;swaggerSpec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;swaggerDocs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see from the code where we defined the &lt;a href="https://spec.openapis.org/oas/latest.html#openapi-specification" rel="noopener noreferrer"&gt;Open API Specification (OAS)&lt;/a&gt; that we will be using for our documentation: the information or details about the API, the servers we will expose it to and the routes in our app where Swagger should look for specifications for each of our API.&lt;/p&gt;

&lt;p&gt;We can also see our &lt;code&gt;swaggerDocs&lt;/code&gt; function that allows the app instance and port to be able to generate the documentation for using the &lt;code&gt;swaggerUi&lt;/code&gt; and &lt;code&gt;swaggerJsdoc&lt;/code&gt; package we installed earlier and serve it at &lt;code&gt;/docs&lt;/code&gt; route. We can also get the JSON format using &lt;code&gt;/docs.json&lt;/code&gt;. Lastly, we will update our &lt;code&gt;server.js&lt;/code&gt; file to include our &lt;code&gt;swaggerDocs&lt;/code&gt; function to always generate and update our docs whenever we run the project.&lt;/p&gt;

&lt;p&gt;The final step before writing out the specification for each endpoint is to add the &lt;code&gt;swaggerDocs&lt;/code&gt; function to our &lt;code&gt;server.js&lt;/code&gt; file so we initiate swagger whenever our app starts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&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;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;morgan&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;morgan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;connect&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/conn.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;userRouter&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;./router/user.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postRouter&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;./router/post.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;swaggerDocs&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;./swagger.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

    &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="cm"&gt;/** middlewares */&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;morgan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tiny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-powered-by&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// less hackers know about our stack&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/** HTTP GET Request */&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home GET Request&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="cm"&gt;/** api routes */&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;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postRouter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="cm"&gt;/** start server only when we have valid connection */&lt;/span&gt;
    &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server connected to 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="nf"&gt;swaggerDocs&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;port&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cannot connect to the server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid database connection...!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing our API Specifications
&lt;/h2&gt;

&lt;p&gt;Now to the main task, we will write the specifications for our API that Swagger will use to generate the documentation for us. We currently have two controllers and router files in our app for &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;post&lt;/code&gt;. Our controllers contain logic for the app while the router carries the endpoint and payload to the controller in form of requests. Let’s start defining the spec!&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;User&lt;/strong&gt; router, we will first import the packages we will be needing in the router file &lt;code&gt;user.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="cm"&gt;/** import all controllers */&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;controller&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;../controllers/userController.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Auth&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;../middleware/auth.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/** All the API routes comes here*/&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will write the spec for each request type viz GET, POST, PUT and DELETE. The specification is a yaml file embedded at the beginning of the route we want to document. Some key points to note are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open API specification instance—written at the beginning of the yaml file.&lt;/li&gt;
&lt;li&gt;The API endpoint—the URL we will use for the request.&lt;/li&gt;
&lt;li&gt;Request Type—indicates if it is a GET, POST, PUT or DELETE request.&lt;/li&gt;
&lt;li&gt;Request Body—for passing our payload to the API.&lt;/li&gt;
&lt;li&gt;Parameters—data we pass through the URL or params to the backend&lt;/li&gt;
&lt;li&gt;Content Type—the type of content we are sending. It is passed to the HTTP header.&lt;/li&gt;
&lt;li&gt;Schema—it contains the type of our request body, the required fields and the properties that the request body can accept.&lt;/li&gt;
&lt;li&gt;Response—the result of the API call we made, it tells us if it failed or succeded and reports errors as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;POST:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/** POST Methods */&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/register':
     *  post:
     *     tags:
     *     - User Controller
     *     summary: Create a user
     *     requestBody:
     *      required: true
     *      content:
     *        application/json:
     *           schema:
     *            type: object
     *            required:
     *              - username
     *              - email
     *              - password
     *            properties:
     *              username:
     *                type: string
     *                default: johndoe 
     *              email:
     *                type: string
     *                default: johndoe@mail.com
     *              password:
     *                type: string
     *                default: johnDoe20!@
     *     responses:
     *      201:
     *        description: Created
     *      409:
     *        description: Conflict
     *      404:
     *        description: Not Found
     *      500:
     *        description: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// register user&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/login':
     *  post:
     *     tags:
     *     - User Controller
     *     summary: Login as a user
     *     requestBody:
     *      required: true
     *      content:
     *        application/json:
     *           schema:
     *            type: object
     *            required:
     *              - username
     *              - password
     *            properties:
     *              username:
     *                type: string
     *                default: johndoe
     *              password:
     *                type: string
     *                default: johnDoe20!@
     *     responses:
     *      201:
     *        description: Created
     *      409:
     *        description: Conflict
     *      404:
     *        description: Not Found
     *      500:
     *        description: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verifyUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// login in app&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/verify':
     *  post:
     *     tags:
     *     - User Controller
     *     summary: Verify a user
     *     requestBody:
     *      required: true
     *      content:
     *        application/json:
     *           schema:
     *            type: object
     *            required:
     *              - username
     *            properties:
     *              username:
     *                type: string
     *                default: johndoe
     *     responses:
     *      201:
     *        description: Created
     *      409:
     *        description: Conflict
     *      404:
     *        description: Not Found
     *      500:
     *        desccription: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verifyUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// authenticate user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GET:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/** GET Methods */&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/{username}':
     *  get:
     *     tags:
     *     - User Controller
     *     summary: Get a user by username
     *     parameters:
     *      - name: username
     *        in: path
     *        description: The username of the user
     *        required: true
     *     responses:
     *      200:
     *        description: Fetched Successfully
     *      400:
     *        description: Bad Request
     *      404:
     *        description: Not Found
     *      500:
     *        description: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// user with username&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PUT:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** PUT Methods */&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/update':
     *  put:
     *     tags:
     *     - User Controller
     *     summary: Modify a user
     *     requestBody:
     *      required: true
     *      content:
     *        application/json:
     *           schema:
     *            type: object
     *            required:
     *              - userId
     *            properties:
     *              userId:
     *                type: string
     *                default: ''
     *              firstName:
     *                type: string
     *                default: ''
     *              lastName:
     *                type: string
     *                default: ''
     *     responses:
     *      200:
     *        description: Modified
     *      400:
     *        description: Bad Request
     *      404:
     *        description: Not Found
     *      500:
     *        description: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// is use to update the user profile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DELETE:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** DELETE Methods */&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * @openapi
     * '/api/user/{userId}':
     *  delete:
     *     tags:
     *     - User Controller
     *     summary: Delete user by Id
     *     parameters:
     *      - name: userId
     *        in: path
     *        description: The unique Id of the user
     *        required: true
     *     responses:
     *      200:
     *        description: Removed
     *      400:
     *        description: Bad request
     *      404:
     *        description: Not Found
     *      500:
     *        description: Server Error
     */&lt;/span&gt;
    &lt;span class="nx"&gt;userRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:userId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing our APIs on Swagger
&lt;/h2&gt;

&lt;p&gt;After finishing our API documentation, we should be able to view our Swagger documentation and test our API using it as well. If you followed till this point, you should have a view like the one below. Our documentation is served on the &lt;code&gt;/docs&lt;/code&gt; route.&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%2F3gt53pqk7la6xvaysk2y.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%2F3gt53pqk7la6xvaysk2y.png" alt="Swagger UI documentation" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will make some requests using the Swagger documentation UI and see the results. &lt;/p&gt;

&lt;p&gt;To create a User:&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%2F5pt7repus94godkquups.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%2F5pt7repus94godkquups.png" alt="A POST request to create a user" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create a Post:&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%2F28kgm3qawpa41l86hadk.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%2F28kgm3qawpa41l86hadk.png" alt="A POST request to create a post" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we have seen from the examples above, we can use the Swagger documentation to test our API, to create, read and modify data in our database. This helps to make the API understandable and easy to integrate by others. Following these examples, we can go ahead and test for other request methods like PUT to update a user or post, GET to read a user or post and DELETE to delete them from the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;We can finally conclude that API documentation is a very important part of the software development cycle and that it aids collaboration and makes user experience seamless. Some of the advantages of Swagger include but are not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synchronizes the API documentation with the server and client at the same pace.&lt;/li&gt;
&lt;li&gt;Allows us to generate REST API documentation and interact with the REST API. The interaction with the REST API using the Swagger UI Framework gives clear insight into how the API responds to parameters.&lt;/li&gt;
&lt;li&gt;Provides responses in the format of JSON and XML.&lt;/li&gt;
&lt;li&gt;Implementations are available for various technologies.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>swagger</category>
      <category>api</category>
    </item>
  </channel>
</rss>
