<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: programequity</title>
    <description>The latest articles on DEV Community by programequity (@programequity).</description>
    <link>https://dev.to/programequity</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%2F553837%2F18c29052-0388-496b-808f-2a61bae80c83.jpg</url>
      <title>DEV Community: programequity</title>
      <link>https://dev.to/programequity</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/programequity"/>
    <language>en</language>
    <item>
      <title>Using Merge Variables from Lob API</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:40:28 +0000</pubDate>
      <link>https://dev.to/programequity/using-merge-variables-from-lob-api-39i5</link>
      <guid>https://dev.to/programequity/using-merge-variables-from-lob-api-39i5</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/nancyluucodes/"&gt;Nancy Luu&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;Lob is a platform that automates the sending of direct mail, making it as easy as sending an email. With the Lob API, you can send an audience file that includes 3rd and 1st party data along with a creative file that has HTML conversion and dynamic parameters. Lob’s HTML templates support the inclusion of dynamic fields, called merge variables, which can be populated individually for each mail piece. Once Lob validates the send address, they forward your file to their print partners who send the prints through USPS. At the end of the process, you’ll receive analytics. This article outlines building dynamically personalized creatives through the use of merged variables as a linchpin of Lob’s utility, allowing for a sophisticated and tailored approach to content generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagram illustrating Lob’s real-time direct mail engagement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokkjtxghzhalvpy631dg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokkjtxghzhalvpy631dg.png" alt="Diagram by Lob.js" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Diagram by Lob.js&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Through Program Equity’s application Amplify, community members can champion campaigns for sustainability, fostering activism, and catalyzing substantive change. We aim to streamline community advocacy by leveraging the Lob API to craft tailored letters for local representatives to receive. The following examples serve as a foundational representation of our application, offering a generalized overview and providing a starting point for potential implementations.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;In the Amplify application, we defined a Vue component (&lt;code&gt;LetterLoad.vue&lt;/code&gt;) to represent the frontend template for composing and displaying letters. Merge variables in HTML templates are placeholders that allow dynamic content insertion during runtime. In Vue components, you can use double curly braces (&lt;code&gt;{{ }}&lt;/code&gt;) to define and utilize merge variables. For instance, if you have an input field in a Vue component, you can capture user input and then use the merge variable syntax to dynamically insert this input into your HTML template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"userInput"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Enter Your Name:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"userName"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"userInput"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"constituentInput"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I'm in support because:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"constituentInput"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"constituentInput"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Using merge variable to display user input dynamically --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;My name is {{ userName }}.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ constituentInput }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;userName&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="c1"&gt;// Initialized to an empty string&lt;/span&gt;
      &lt;span class="na"&gt;constituentInput&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;While Amplify had various Express routes, such as those for fetching specific letter templates and validating addresses via the Lob API, let’s narrow our focus to a post route named &lt;code&gt;/generateLetter&lt;/code&gt; and dive into the details of merge variables to stay on track. Here is the whole request but let’s break this down step by step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;Lob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lob&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&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;lobApiKey&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;LOB_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assuming you have a Lob API key stored in an environment variable&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lob&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;Lob&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;lobApiKey&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="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;/generateLetter&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;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="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;constituentInput&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 against Lob's constraints&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergeVariablesString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;constituentInput&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;mergeVariablesString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25000&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;400&lt;/span&gt;&lt;span class="p"&gt;)&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;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;Merge variables payload exceeds Lob's character limit.&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;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Creating the HTML content of the letter with merge variables&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letterBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;p&amp;gt;My name is {{userName}}.&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;{{constituentInput}}&amp;lt;/p&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Creating Lob letter&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letter&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;lob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="s1"&gt;Generated Letter&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;Recipient Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address_line1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Address Line 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address_line2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Address Line 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address_city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient City&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient State&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address_zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Zip Code&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;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letterBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&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;merge_variables&lt;/span&gt;&lt;span class="p"&gt;:&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;constituentInput&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;// Assuming you want to send back Lob's response or any other relevant data&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;letterId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letter&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;expectedDeliveryDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expected_delivery_date&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;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;json&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="s1"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before anything is done within the route we need to ensure that the merge variables payload is within Lob’s constraints. Any type of value is accepted as long as the object is valid JSON. You can use strings, numbers, booleans, arrays, objects, or null. The max length of the object is 25,000 characters.&lt;/p&gt;

&lt;p&gt;(Note: Your variable names cannot contain any whitespace or any of the following special characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;!,&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;,&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(,&lt;/span&gt; &lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="o"&gt;*,&lt;/span&gt; &lt;span class="o"&gt;+,&lt;/span&gt; &lt;span class="o"&gt;,,&lt;/span&gt; &lt;span class="o"&gt;/,&lt;/span&gt; &lt;span class="o"&gt;;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;,&lt;/span&gt; &lt;span class="o"&gt;=,&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="o"&gt;@,&lt;/span&gt; &lt;span class="o"&gt;[,&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="o"&gt;^,&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;|,&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~.)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mergeVariablesString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;constituentInput&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;mergeVariablesString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25000&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;400&lt;/span&gt;&lt;span class="p"&gt;)&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;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;Merge variables payload exceeds Lob's character limit.&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;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This post request expects a request body to contain the userName and constituentInput fields we defined in the component above. The route then creates HTML content for the letter using these variables and submits the data in the ‘file’ field to Lob API to generate the letter. Remember, it is important to utilize the merge variable syntax within the letter body to ensure dynamic content is correctly passed to Lob API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letterBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;p&amp;gt;My name is {{userName}}.&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{constituentInput}}&amp;lt;/p&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next section uses the Lob SDK to create a new letter. The lob.letters.create method is called with an object containing various parameters. (Note: to and from could also have been merge variables.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letter&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;lob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="s1"&gt;Generated Letter&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;Recipient Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address_line1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Address Line 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address_line2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Address Line 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address_city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient City&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient State&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address_zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient Zip Code&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;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letterBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;color&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;merge_variables&lt;/span&gt;&lt;span class="p"&gt;:&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;constituentInput&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;And finally, assuming the Lob Letter creation is successful, the server responds with a JSON object containing information about the generated letter such as its letterId and the expectedDeliveryDate. If an error occurs, it is caught and logged to the console and an HTTP status of 500 is sent to the client.&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;// Assuming you want to send back Lob's response or any other relevant data&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;letterId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letter&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;expectedDeliveryDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expected_delivery_date&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;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;json&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="s1"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Utilizing the power of Lob’s API for dynamically personalized direct mail campaigns opens up a world of possibilities for community advocacy and engagement. I hope this article can serve as a building block for your understanding of Lob and its capabilities. I encourage you to explore innovative ways to leverage technology and effect positive change in your communities. It truly shines when used for good!&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//www.amplifyadvocacy.org"&gt;Amplify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.lob.com/#tag/Letters/operation/letter_retrieve"&gt;Docs - Lob&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>community</category>
      <category>programming</category>
    </item>
    <item>
      <title>GitHub Actions — Labeler vs Github-script</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:40:24 +0000</pubDate>
      <link>https://dev.to/programequity/github-actions-labeler-vs-github-script-d04</link>
      <guid>https://dev.to/programequity/github-actions-labeler-vs-github-script-d04</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/julie-gunawan/"&gt;Julie Gunawan&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m writing this blog to continue the previous blog on GitHub Actions. As I previously mentioned, I was involved in the ProgramEquity, joining the DevOps team. Program Equity is a non-profit organization that connects tech workers to skill-based volunteering workflows on open-source climate tech projects. The organization works on the social impact through building the web app called Amplify. Furthermore, this project digitally transforms how Advocacy nonprofits onboard volunteers, fundraise and automate civic engagement. To learn more about Program Equity, visit their &lt;a href="https://www.linkedin.com/company/programequity/"&gt;LinkedIn page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ProgramEquity has three teams: Front-End, Back-End, and DevOps, where each team is responsible for solving tickets. One of the tickets I was working on with ProgramEquity involved creating a labeler action. This action assigns ‘new contributor’ label to individuals who are new to the repository. The logic behind this starts with checking if users never created a pull request or an issue, in a specific repository, under their account. If there is none found, then the action will label the user as a new contributor. Otherwise, it does nothing.&lt;/p&gt;

&lt;p&gt;Several individuals, including myself and Satoshi, contributed to resolving this ticket. In this blog, I will focus on Satoshi’s solution, because as a team, we decided to merge his code into the main branch of the Amplify App. After I briefly discuss Satoshi’s Github Actions workflow, I will talk about why my solution is not the best. You can read and follow &lt;a href="https://dev.to/satoshi-sh"&gt;Satoshi’s blog here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Satoshi decided to use a GitHub Action from the marketplace called &lt;code&gt;[github-script](https://github.com/marketplace/actions/github-script)&lt;/code&gt;. This action proves handy for quickly incorporating a script into your workflow. This Github action provides various objects such as github, context, core, glob, io, exec, and require which are often helpful to obtain information on each API call.&lt;/p&gt;

&lt;p&gt;There were two objects that were used in this Github Action workflow: ‘github’ and ‘context’. Github was used to obtain information of a specific user (eg. user “A”), such as all issues or pull requests listed under each repository that is created by user A.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4hc332ayq045zb19g9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4hc332ayq045zb19g9p.png" alt="Image description" width="482" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Img. 1:&lt;/strong&gt; Illustration on how information contained in github object under Labeler Action&lt;/p&gt;

&lt;p&gt;Additionally, the context object was used to access detailed information in the repository where the GitHub Action is running, such as the user that is logged in and issue number of this repository. The content of the context object is obtained from the github action &lt;a href="https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts"&gt;toolkits&lt;/a&gt;. Based on the code &lt;a href="https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts"&gt;here&lt;/a&gt;, context object contains information such as payload, eventName, sha ref, workflow, action, actor, job, runAttempt, runNumber, runId, apiUrl, serverUrl, and graphqlUrl. The payload variable contains a Webhook payload object that triggered the workflow. Webhook payload object itself contains several other information such as repository, issue, pull_request, sender, action, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzokyt851j46zbgxozsn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzokyt851j46zbgxozsn.png" alt="Image description" width="484" height="512"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Img 2:&lt;/strong&gt; Context object has a variable called payload, which is a WebhookPayload object.&lt;/p&gt;

&lt;p&gt;I will let Satoshi explain about the action he created, in his blog. You could also learn more by looking into the actual code &lt;a href="https://github.com/ProgramEquity/amplify/commit/63d33f4e2742f51045f59319559b1ccff3926b0c"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, I used Github Actions from the marketplace called labeler. The way you use this action is very simple. You need to create a file called labeler.yml in the ‘.github/workflows’ folder, while at the same time, you need to create a configuration file called ‘labeler.yml’ under the root folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vakub1yz4mtiwkjk2jz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vakub1yz4mtiwkjk2jz.png" alt="Image description" width="449" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Img 3:&lt;/strong&gt; The labeler action starts from line 17. By default, configuration path doesn’t need to be declared as it already points to ‘.github/labeler.yml’&lt;/p&gt;

&lt;p&gt;There is a limitation for this labeler action. After testing a couple times, I realized that this action only labels the events based on file changes specified in the configuration file, the ‘labeler.yml’ file in the root folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zk9mxzivpzn0vbiat25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zk9mxzivpzn0vbiat25.png" alt="Image description" width="502" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Img 4:&lt;/strong&gt; Configuration file contains which label will be attached to the specific event, based on file change&lt;/p&gt;

&lt;p&gt;You can read about the details on how to create the configuration file based on the &lt;a href="https://github.com/actions/labeler"&gt;original repo&lt;/a&gt;. You can also look into my commit history and the code &lt;a href="https://github.com/JulieGunawan/git-action-test"&gt;here&lt;/a&gt;, to see how it results in different labels, for pull request events. However, creating issues doesn’t trigger the GitHub Actions as the action only detects changes in the repo file, through ‘git push’ command to the pull-request.&lt;/p&gt;

&lt;p&gt;In conclusion, github-script is the closest GitHub action solution to apply the contributor label based on a user’s activities. Github labeler action only detects the file changes, but it is not able to check on the user information. It is possible to create a GitHub action labeler workflow through GitHub CLI. Ethan, one of the ProgramEquity mentors, suggested using the if condition, as you can see in the snapshot below, as well as the ticket &lt;a href="https://github.com/ProgramEquity/amplify/issues/625"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ygzambkk1vorkcusjup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ygzambkk1vorkcusjup.png" alt="Image description" width="512" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Img 5:&lt;/strong&gt; Suggestion on how to create github action workflow for labeler action&lt;/p&gt;

&lt;p&gt;In conclusion, there are many Github Actions available in the marketplace, however, you might need to test them one by one, to check if they are the right action for the purpose you are looking for. Otherwise, another suggestion will be to create your own Github Actions. You can also post your created github actions into the marketplace, to be used by others.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>github</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Enhancing Civic Engagement and Communication with Lob: Letter Madlib Customization</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:40:21 +0000</pubDate>
      <link>https://dev.to/programequity/enhancing-civic-engagement-and-communication-with-lob-letter-madlib-customization-3j2a</link>
      <guid>https://dev.to/programequity/enhancing-civic-engagement-and-communication-with-lob-letter-madlib-customization-3j2a</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/christy-g-b1b1295a/"&gt;Christy G&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the fast-paced world of civic technology, the need for effective and personalized communication is paramount. Our recent initiative to create more feedback from users aims to tell a deeper, more engaging story. ProgramEquity is this amazing organization that bridges the gap between tech workers and skill-based volunteering. They're all about making a social impact, and they do this through a web app called Amplify. Our goal was straightforward: to create more feedback from our users to tell a deeper story. We recognized that constituents often face challenges in articulating how specific policies affect them. The process of brainstorming and recalling reasons can be time-consuming and sometimes daunting. We wanted to simplify this process, making it both quick and specific.&lt;/p&gt;

&lt;p&gt;Our approach involved a blend of frontend and backend solutions. On the backend, I leveraged Lob's merge variable feature. This powerful tool allows us to dynamically insert specific user data into our letters, ensuring that each message is tailored to the recipient’s personal experience and views. On the frontend, the key was to efficiently gather user input to populate these merge variables. We designed an intuitive interface where users can easily select options that best describe their perspectives and experiences. This madlib-style design offers a range of predefined options, making the process of expressing views both specific and effortless. We have integrated snippets from my PR to provide a glimpse into the technical aspects of this implementation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1izfnivoi37bcl99potr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1izfnivoi37bcl99potr.png" alt="Image description" width="433" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This integration with Lob is a game-changer, offering customization at scale. Our Madlib design approach simplifies the process of personal feedback. By providing options, we make it easier for constituents to identify and articulate their views, as opposed to the traditional method of recalling and brainstorming. This not only saves time but also encourages more precise and meaningful communication.&lt;/p&gt;

&lt;p&gt;In conclusion, this experience with ProgramEquity and Amplify has been more than just a project – it's been a pivotal chapter in my professional journey. I hope sharing my experience has shed some light on the intricacies and rewards of working in civic tech. It's been a privilege to contribute to something that not only challenges me but also makes a tangible impact in the world.&lt;/p&gt;

</description>
      <category>community</category>
      <category>automation</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Importance of Reading Between the Errors: My 'Prettier' Saga</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:40:17 +0000</pubDate>
      <link>https://dev.to/programequity/the-importance-of-reading-between-the-errors-my-prettier-saga-fip</link>
      <guid>https://dev.to/programequity/the-importance-of-reading-between-the-errors-my-prettier-saga-fip</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/mayejesuorobo/"&gt;Maye (Naomi) Jesuorobo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently, I decided to take the plunge into the world of open-source contribution. I was fortunate to meet a kind and generous group of people at Program Equity and work on their app, Amplify. Amplify is a U.S based, open-source, non-profit organization created for individuals to take the initiative in being part of an actionable step in the efforts to protect against climate change and find Civil Rights causes and Indigenous People’s causes that they support.&lt;/p&gt;

&lt;p&gt;While working on my chosen endpoint API, I encountered an unexpected challenge. After adding a Jest test into my code and attempting to push the changes for a pull request, I was surprised to see that I couldn't proceed. It was a unique situation for me, given my familiarity with similar workflows in the past.&lt;/p&gt;

&lt;p&gt;Initially, I quickly skimmed through the error message to identify the issue, only to discover there were two messages. The first, a lengthy one, focused on reapplying my stash, while the second, smaller but would be crucial, indicated that 'prettier' had not been formatted. Unfortunately, I overlooked the second error message and concentrated solely on the first.&lt;/p&gt;

&lt;p&gt;In an effort to sync my branch with the main one, as suggested by the initial error message, I executed a series of Git commands, including 'git stash,' 'git rebase,' and 'git stash apply.' Despite my persistent attempts, I consistently encountered an error, hinting at uncommitted changes in my stash and other issues. Frustrated, I diligently recommitted changes, experimenting with various git commands to reapply stashes, and repeatedly attempted to push, only to hit the same roadblock.&lt;/p&gt;

&lt;p&gt;It wasn't until I was on the verge of seeking assistance from a project maintainer - typing out my message to explain the problem I was facing - that I realized I hadn't thoroughly read the error messages. Upon revisiting them, I decided to scrutinize the second, shorter error message about 'prettier'. While I knew 'prettier' was commonly used for code formatting, I hadn't realized it could prevent pushes and cause such complications.&lt;/p&gt;

&lt;p&gt;Determined to resolve the issue, I delved into understanding how to resolve the issue the error message was referring to and discovered that I needed to apply 'prettier --write .' due to the 'Prettier' package before pushing. Prettier, as it turns out, plays a vital role in organizing the codebase for improved readability. After spending over an hour attempting to force a push, I finally yielded to reading the error message in its entirety. Surprisingly, applying the 'Prettier' package proved to be the missing piece.&lt;/p&gt;

&lt;p&gt;This experience, while initially frustrating, served as a potent reminder of a fundamental lesson – the significance of thoroughly reading error messages, regardless of their length. Despite nearly two hours of struggling to do things my way, a simple acknowledgment of the error message's guidance swiftly resolved the issue. Lesson learned: patience and careful attention to error messages can save a considerable amount of time and effort in the development process.&lt;/p&gt;

&lt;p&gt;References: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thenounproject.com/icon/glasses-371891/"&gt;Icon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>github</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Passport.js, a Security Solution for Small Startups and Large Enterprises Alike</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:39:33 +0000</pubDate>
      <link>https://dev.to/programequity/passportjs-a-security-solution-for-small-startups-and-large-enterprises-alike-fbn</link>
      <guid>https://dev.to/programequity/passportjs-a-security-solution-for-small-startups-and-large-enterprises-alike-fbn</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/brian-segura-015/"&gt;Brian Segura&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  I’m hoping that my first issue in open source will help you solve some of yours.
&lt;/h3&gt;

&lt;p&gt;Some of you might be wondering “What is Passport.js?” and “How can it solve our software security concerns?” Others might be wondering why our software even needs to be secure in the first place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz2jksnbi5okkm4ykps3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz2jksnbi5okkm4ykps3.png" alt="Image description" width="500" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well for starters, cyber crime has cost companies around the world billions of dollars in direct financial losses, business disruptions, reputational damage and more. Without having the right measures in place, we can leave our users and our company’s sensitive data exposed to malicious threats that could gain access to our applications (even from the inside of a company).&lt;/p&gt;

&lt;p&gt;The name of our startup app is Amplify by ProgramEquity. It enables individuals across the nation to come together to find social justice causes that they can support. It reduces the friction of getting in contact with politicians to help said cause.&lt;/p&gt;

&lt;p&gt;I chose the daunting task of providing security around our new control panel feature. We needed a way in which an &lt;code&gt;admin&lt;/code&gt; user could authenticate themselves in order to view their campaigns, edit critical information, and have more insights to behind-the-scenes info when compared to a normal volunteer or constituent who shouldn't have those capabilities.&lt;/p&gt;

&lt;p&gt;There are many frameworks and libraries to choose from when it comes to authentication and authorization like Oauth2orize, Auth0, Feathers, Keycloak, and Passport.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyuujakti7r5ely38p6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyuujakti7r5ely38p6o.png" alt="Image description" width="275" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our app, we chose to go with Passport.js. Passport is known for its simplicity, its ability to be extremely modular, and it has many different authentication strategies. Many mainstream apps you use allow you to sign in with Google, Facebook, Twitter, or just your email. These are some of the many authentication strategies available through Passport.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiivkdi717rayo80y23lm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiivkdi717rayo80y23lm.png" alt="Image description" width="512" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this version of our app, we went with Passport’s LocalStrategy for handling local authentication via username and password. Which means that you can locally create an account and log in by providing just your username and password.&lt;/p&gt;

&lt;p&gt;Implementing Passport.js with local strategy and integrating it into our project involved a lot of moving parts. Let's delve into a high-level overview of some of these key components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create User Authentication Routes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create routes in your application to handle user authentication tasks, such as logging in and signing up.&lt;/li&gt;
&lt;li&gt;These routes usually include endpoints like /login, /signup, /logout, etc.&lt;/li&gt;
&lt;li&gt;In the route handlers, you’ll receive user credentials (in our case, email/password) from the request body and pass them to Passport.js for authentication.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mock User Model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a user model or use an existing one to represent the user data in your application.&lt;/li&gt;
&lt;li&gt;This model should include fields such as username, email, hashed password, etc.&lt;/li&gt;
&lt;li&gt;Ensure that the user model provides methods for interacting with your database to perform CRUD operations like creation, retrieval, updating, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hashing and Verifying Passwords
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Before storing passwords to your database, always hash them using a secure hashing algorithm like Bcrypt.&lt;/li&gt;
&lt;li&gt;When a user signs up or updates their password, hash the password before storing it in the database.&lt;/li&gt;
&lt;li&gt;During login, hash the password provided by the user and compare it with the hashed stored in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Managing Passport’s Session Middleware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Configure Passport.js to use sessions for managing authentication state across requests and improve user experience.&lt;/li&gt;
&lt;li&gt;Use session middleware like express-session to handle session management in your Express app.&lt;/li&gt;
&lt;li&gt;Initialize Passport.js and configure it to use the session middleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementing Middleware Protection
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Define middleware functions to protect routes that require authentication.&lt;/li&gt;
&lt;li&gt;Use Passport.js middleware to authenticate requests using the local strategy before allowing access to protected routes.&lt;/li&gt;
&lt;li&gt;If authentication fails, redirect the user to the login page or return an error response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before this experience with ProgramEquity and their open-source community, I only had minimal exposure to Passport.js. However, with this time around, I had to really get in the weeds, figure out how it all tied together and take a deep dive into the documentation to provide our team a way forward in terms of securing our app. I’m happy that I have had the exposure of how authentication/authorization is important for all apps of all sizes, from small personal side projects to large enterprise applications.&lt;/p&gt;

&lt;p&gt;In conclusion, thanks to the help of a fellow contributor and the guidance of a mentor who I now can call a friend, I was able to find out what we needed and how to implement it into our codebase. This was also my first time contributing to open source and my first time diving into a live codebase. I encourage anyone on the fence about diving into an open-source project to take the plunge. It’s been an amazing learning experience and I’ve met incredible people along the way. Remember that as cliche as it sounds, the magic always happens outside of our comfort zone. Happy coding, everyone!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S here’s a resource!&lt;/strong&gt;&lt;br&gt;
I saw this incredibly informative video that explains how Passport works in great detail which dramatically increased my understanding of what I was doing. As usual, freeCodeCamp comes in clutch with the 🔥content, check it out here:&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=F-sFp_AvHc8&amp;amp;t=7125s"&gt;User Authentication in Web Apps (Passport.js, Node, Express)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Learning PostgreSQL on the Fly: Building and Integrating Tables, Models, and Migrations Using Knex</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:38:56 +0000</pubDate>
      <link>https://dev.to/programequity/learning-postgresql-on-the-fly-building-and-integrating-tables-models-and-migrations-using-knex-38j8</link>
      <guid>https://dev.to/programequity/learning-postgresql-on-the-fly-building-and-integrating-tables-models-and-migrations-using-knex-38j8</guid>
      <description>&lt;p&gt;By Kendall Trudick | &lt;a href="https://github.com/ktrudickm"&gt;GitHub&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/kendalltrudick/"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I think it’s safe to say that a lot of ‘newer’ software engineers are daunted at the idea of jumping into and contributing to open source projects. And I understand that, it’s something that I faced as well. However, I came to the point where I realized that there lie so many advantages to doing so and that I needed to just throw myself into it. So, fast forward to now, I can confidently say it truly is not as bad as you may think! So, with all of that being said, I’ll dive a bit into my experience jumping-in and also touch more on the technical side of things, especially in regards to working with PostgreSQL databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti4o8si2ux087bormjde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti4o8si2ux087bormjde.png" alt="Image description" width="720" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;The open-source project I took on is one run by ProgramEquity on their &lt;a href="https://github.com/ProgramEquity/amplify"&gt;Amplify&lt;/a&gt; app. This app was created for users to take the initiative in being part of an actionable step in the efforts to protect against climate change. Being someone who felt more ‘comfortable’ with front-end development, I made it my goal to take on more back-end heavy tasks. With that in mind, the first, and arguably more valuable, task/issue I decided to take on was building an admin table using PostgreSQL. This issue was part of the control panel feature of the app. Below are the details of the issue itself:&lt;/p&gt;

&lt;h3&gt;
  
  
  Details of the Issue
&lt;/h3&gt;

&lt;p&gt;Now, the challenge this presented me with was due to the fact that I have never worked with PostgreSQL before, and so I knew there would be a learning curve. But again, I went into this intending to push myself out of my comfort zone. That being said, I think having worked with other databases provided a solid enough foundation to be able to comprehend the underlying technology and be able to pick things up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Learning The Basics/Familiarizing Myself
&lt;/h3&gt;

&lt;p&gt;The first thing I did was make sure I looked over and understood the codebase. This involved sifting through the code, especially the backend code, studying the other database tables and models already set up, and connecting the dots. You can learn a lot from preexisting code and this is definitely something that helped me tremendously.&lt;/p&gt;

&lt;p&gt;From there, I made sure I understood all of the attributes needed for the admin table, the table I needed to build. These included things like first name, last name, email, password, active status, date created, and date last updated. Having an understanding of what types of attributes the table requires is also incredibly helpful as you dive into the documentation and formulate your solution and approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knex Migration
&lt;/h3&gt;

&lt;p&gt;In addition to Postgres, the Amplify app made use of Knex, which is a tool used for facilitating the database connection using postgres, the migrations, and seed data. (Again, another tool I have no history working with). To break it down, Knex handles database migrations which is the process of managing changes to a database schema over time. This could involve creating new tables, modifying existing ones, or even deleting tables. This is exactly what I used to build my admin table.&lt;/p&gt;

&lt;p&gt;For this project, knex was already set up and initialized so all I had to do to create the table migration was run:&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="nv"&gt;$ &lt;/span&gt;npx knex migrate:make create-admin-table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing so created its own separate file in the migrations folder with its relevant timestamp. So, the file name was as follows: migrations/20231204193051_create-admins-table.js. After generating the migration, your file will look something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhkreksvipetp0jdnn74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhkreksvipetp0jdnn74.png" alt="Image description" width="720" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, I updated the content in the file to contain my relevant information such as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oiuen3tk5xqgbnush0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oiuen3tk5xqgbnush0l.png" alt="Image description" width="720" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve filled out the migration file with the information and details you need specific to the table you are creating, you can then run:&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="nv"&gt;$ &lt;/span&gt;npx knex migrate:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the Model
&lt;/h3&gt;

&lt;p&gt;So once I created the migration for the admin table, I then needed to create the model. For the models, objection was being used for the other pre-existing models, so this is what I followed. Objection is an object-relational mapping (ORM) library for Node.js that is specifically designed to work with relational databases, like Postgres. It provides an object-oriented interface for interacting with databases, allowing developers to work with JavaScript objects rather than raw SQL queries.&lt;/p&gt;

&lt;p&gt;With objection, if you are creating multiple tables and using knex in order to handle the connections with all of the tables, it’s a good idea to create a Base Model which you can use to build all of your other models. Here is the preexisting base model in the app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnf4v7hpzso6kt1msf3x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnf4v7hpzso6kt1msf3x7.png" alt="Image description" width="720" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the base model is set up, you can easily create new models that are derived from this base model. Here is the model I set up for the admins table in admins.js file under the models directory:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcrobhzuiz6jvh6zdafq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcrobhzuiz6jvh6zdafq2.png" alt="Image description" width="720" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Seed Data
&lt;/h3&gt;

&lt;p&gt;Lastly, the seed data needed to be put together. Now, I went about this in maybe the ‘lazy’ way, since I just referenced the other existing seed files and modified it to match my admin table. However, this can be done easily with knex. To do so, you can 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 knex seed:make seed-admins-table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this command creates a seed-admins-table.js file within a seeds directory, if the seeds directory already exists, which in my case it did. If it doesn’t exist, it will automatically create the directory for you and place the seed-admins-table.js file within it. Once the file is created, you can go ahead and populate the file with the necessary information. Below is what the seed file looked like for the admins table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbuym6jo37qwqqqn4dnm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbuym6jo37qwqqqn4dnm.png" alt="Image description" width="720" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to notice here is how the password itself is being encrypted using the encrypt.js file in the lib directory. For the seed data, we are simply encrypting a password of “password”.&lt;/p&gt;

&lt;p&gt;And that's essentially all there is to it! At first glance, it all may seem a bit confusing, but I promise as you work through it and take it step by step, you come to find that it is very intuitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Note on Collaboration
&lt;/h3&gt;

&lt;p&gt;Open source thrives on collaboration, bringing together diverse skills for collective success. In the context of the Amplify app, collaborative efforts were pivotal in integrating the PostgreSQL admin table.&lt;/p&gt;

&lt;p&gt;Collaborate actively by submitting pull requests and seeking feedback. Engaging with the community refines implementations, ensures code quality, and aligns changes with project standards. Embrace feedback to enhance the overall codebase.&lt;/p&gt;

&lt;p&gt;Most importantly: it’s OK to ask for help! When dealing with unfamiliar technologies like PostgreSQL and Knex, don’t hesitate to ask for help. Utilize the community’s wealth of knowledge to navigate challenges efficiently. Seeking assistance is a proactive approach to problem-solving and accelerates the learning curve. There are always people to learn from.&lt;/p&gt;

&lt;p&gt;In summary, open source success hinges on collaboration. Actively engage with the community, seek feedback, and embrace assistance when needed. This collective effort makes open source a rewarding environment for skill expansion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;p&gt;In the world of open-source adventures, tackling the admin table integration with PostgreSQL&lt;/p&gt;

&lt;p&gt;in the Amplify app taught me that the best learning happens when you jump into something new. Turns out, dealing with tech challenges, like using Knex, Objection, and Postgres, is not as hard as it may at first seem.&lt;/p&gt;

&lt;p&gt;Ultimately, this journey highlights that most things are simpler than we make them out to be. It’s a reminder of how important it is to keep learning and adapting, especially if that involves contributing to open-source projects.&lt;/p&gt;

&lt;p&gt;For fellow developers thinking about joining open-source projects, take the leap! It’s a fantastic chance for personal and professional growth in the ever-changing world of tech, and I almost guarantee you won't regret the skills you learn, the insight you gain, and the confidence you attain. Not to mention the people you meet.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/"&gt;Postgres Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knexjs.org/"&gt;Knex Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vincit.github.io/objection.js/"&gt;Objection Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ProgramEquity/amplify"&gt;ProgramEquity, Amplify App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thenounproject.com/icon/bird-1637609/"&gt;Icon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgressql</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Enhancing Security: Passport.Js</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 07 Feb 2024 18:38:29 +0000</pubDate>
      <link>https://dev.to/programequity/enhancing-security-passportjs-37if</link>
      <guid>https://dev.to/programequity/enhancing-security-passportjs-37if</guid>
      <description>&lt;p&gt;By &lt;a href="https://www.linkedin.com/in/konny-guo-460899206/"&gt;Konny Guo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi there, fellow developers! If you are looking to up your security game for your website then you came to the right place. In this blog, we'll go through the process of authenticating websites with Passport.js. Before we get started, make sure you've got an intermediate understanding of Node.js, Express, and a some basic understanding of sessions and authentication. &lt;/p&gt;

&lt;p&gt;In today’s digital world, a ton of data is stored and we want to protect that data from any threats that can cost companies millions in damages. With the risk of data exposure, implementing robust security measures becomes paramount to a company’s success. Amplify provides users with a platform to bring attention to their social causes. This necessitated the implementation of secure authentication for admin users that are using the site.&lt;/p&gt;

&lt;p&gt;Among the expansive number of authentication frameworks available, we chose to use Passport.js. With support for third-party providers like Meta, Google, Twitter, and more, Passport.js has enough flexibility to be used by many developers. Due to its simplicity, modularity, and flexible authentication strategies. Strategies are ways that a user can be logged in. Passport.js was ideal for what we do because we can choose to implement one strategy then change into another strategy or even use multiple strategies at once. For example, we can start off with a Passport Google strategy which makes user able to log into the site with their Google accounts and even add other strategies along the way.&lt;/p&gt;

&lt;p&gt;For Amplify, our mentor suggested that myself and a fellow developer used the Passport's LocalStrategy which focuses on local authentication of username and password. We are able to create accounts right there on the spot without the use of any party providers like Meta, Google, Twitter, and more which can be added later on. We also implemented sessions and middleware which worked together in aiding Passport’s authentication. The middleware helps protect users from being able to go into a specific route. The middleware ensures that a user is ready for a certain action. For instance, if a user was logged in, they should never be able to go to the login page as they are already logged in so they should automatically be routed to the dashboard should they be logged in. If a user is not logged in then they should not be able to access any of the pages that a login user should access. Then we have sessions which allows the user to stay logged in and can time them out if they are gone for too long.&lt;/p&gt;

&lt;p&gt;During my time working on ProgramEquity's open source project Amplify, I delved into the intricacies of Passport.js by reading documentations and connecting the code with common implementations used beside it like middleware and sessions. This experience proved invaluable as I worked through a large application and implemented my code alongside it.&lt;/p&gt;

&lt;p&gt;In conclusion, contributing to Amplify’s codebase allowed me to challenge myself to learning and understanding real world development issues. The experience with understanding code security will prove very useful in the present and near future. I appreciate the help from my peers and mentor that I received along the way to overcome some of the challenges in the beginning. I hope that this blog helped improve your understanding as well.&lt;/p&gt;

</description>
      <category>security</category>
      <category>programming</category>
      <category>community</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to use LOB API and customize a form for a letter</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 31 May 2023 10:35:14 +0000</pubDate>
      <link>https://dev.to/programequity/how-to-use-lob-api-and-customize-a-form-for-a-letter-2000</link>
      <guid>https://dev.to/programequity/how-to-use-lob-api-and-customize-a-form-for-a-letter-2000</guid>
      <description>&lt;p&gt;&lt;em&gt;- Saumil Dhankar&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brief note on LOB API&lt;/strong&gt;&lt;br&gt;
     LOB API helps in automation of direct mails. Check out documentation on how to access the API using GET, POST, PUT and DELETE commands. We can even take help from POSTMAN to make these requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;br&gt;
For this blog, we are using the PostgreSQL database to store information about campaigns, constituents, letters and stripe payments and using knex.js to query the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amplify platform&lt;/strong&gt;&lt;br&gt;
    Amplify platform improves accessibility for civic engagement. This blog will be focused on the feature where we use LOB API to request information about the letters to be sent to representatives. More specifically, we will edit the parameters in our API requests. The users will also have the option to edit the letter content on the front end. The letter is displayed on the platform for the users to customize before proceeding to submission and payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other tools needed&lt;/strong&gt;&lt;br&gt;
    We are using Codespaces to quickly set up our environment. In case there are issues with Codespaces, we can set up the dev environment locally as well. For Windows OS, I learnt about a few helpful packages like cross-env (helps in making npm scripts cross platform) and dot-env (helps with env variables). We would also need the cicero api key for testing purposes which can be updated in your .env file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizing form for a letter and using LOB API (Body)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue background&lt;/strong&gt;&lt;br&gt;
    We would like users to have the option to customize their letters. For this functionality, we would create a template with lowest possible cardinality which would cover all the scenarios. As a preliminary step on the front-end, we will capture user input using vue form and on the back-end we will add required parameters in the API requests and possibly update our database. The letter template would help keep our requests to the database at an optimal level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend changes&lt;/strong&gt;&lt;br&gt;
To facilitate users adding information to customize their letter, we are using v-textarea component to capture user input which will then be linked to a POST request and finally sent over to the back-end (See issue #370 for details related to the vue form). This issue gave me exposure to vue code and helped me learn how it integrates with backend and how to write the unit and integration tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend changes&lt;/strong&gt;&lt;br&gt;
We would update the body of our post request to create Letter API which is part of LOB API such that it contains a merge variable. Following line would be updated: Line 162 and the new code would look like:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const { description, to, from, template_id, merge_variable, sessionId } = req.body || {}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This change would be part of issue #183. Postman student learning program really helped me get up to speed with using APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Looking up the data in tables in PostgreSQL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider using knex.js to query the database for testing as well as other requirements. I learnt a lot while researching how to use knex.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Used Vue.js to make changes on the front-end.&lt;/li&gt;
&lt;li&gt;Used POSTMAN to make API calls to LOB API.&lt;/li&gt;
&lt;li&gt;Used knex.js for making database queries and stored data in a Postgres database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We learnt about the helpful tools available in Vue.js, POSTMAN, Postgres and knex.js to customize an application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amplify-app-production.herokuapp.com/"&gt;Amplify platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.lob.com/"&gt;LOB API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/"&gt;PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuejs.org/guide/introduction.html"&gt;Vue.js docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/"&gt;NPM documentation&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next steps (pending completion)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add merge variable to createLetter object in LOB API&lt;/li&gt;
&lt;li&gt;Updating API ecosystem and database (PostgreSQL)&lt;/li&gt;
&lt;li&gt;Encryption&lt;/li&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to use Postman for API testing</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 31 May 2023 10:28:30 +0000</pubDate>
      <link>https://dev.to/programequity/mastering-api-testing-with-postman-a-comprehensive-guide-for-frontend-and-backend-developers-193</link>
      <guid>https://dev.to/programequity/mastering-api-testing-with-postman-a-comprehensive-guide-for-frontend-and-backend-developers-193</guid>
      <description>&lt;p&gt;&lt;em&gt;- Teri Eyenike&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Testing web applications, both frontend and backend project, is important to check if the code written meet the criteria and standard before deployment before it reaches the user. During the software development lifecycle of a product, it is essential to find minor and major issues with the app, like identifying bugs within the code and fixing it promptly with the help of professionals like quality and assurance (QA) testers.&lt;/p&gt;

&lt;p&gt;Postman is a collaborative API platform developers use to test, monitor, design, and build their APIs with a user interface (UI). According to businesswire, more than 20 million developers and 500,000 organizations use Postman, which only shows its success as a reliable platform for API testing.&lt;/p&gt;

&lt;p&gt;In this article, you will learn and explore the various uses of Postman and how it can help you become productive as a developer, no matter your stack.&lt;/p&gt;

&lt;p&gt;Let’s get started&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is an API?&lt;/strong&gt;&lt;br&gt;
Application programming interface (APIs) are everywhere, and it is the bedrock for how applications communicate with one another; they enable integration and automation using a set of defined protocols, HTTPS from the sending a request and receiving a response through a network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjcw2q8efe0q35ds1dbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjcw2q8efe0q35ds1dbn.png" alt="Image description" width="790" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete source code for this project is in this repo.&lt;br&gt;
Prerequisites&lt;/p&gt;

&lt;p&gt;The following are required to complete this tutorial:&lt;br&gt;
Download the Postman app&lt;br&gt;
Basic knowledge of Node.js&lt;br&gt;
Have Node installed on your local machine&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building an API with Express.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Express is an unopinionated web framework built on top of Node.js, the JavaScript runtime environment. Using Express to create APIs is fast.&lt;/p&gt;

&lt;p&gt;Open your terminal and run this command to create a new directory:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir api-testing&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, navigate to the directory and initialize the directory with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd api-testing 

npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command accepts all the defaults with the -y flag, which creates a package.json file.&lt;/p&gt;

&lt;p&gt;With the package.json file created, install these dependencies:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install express&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;express: is used to create a server&lt;/p&gt;

&lt;p&gt;The next package to install is nodemon which would be a dev dependency. That means it is only required to run during local development and testing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install nodemon -D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Configure the package.json file&lt;br&gt;
Change the values in the scripts section of the file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 ...
 "scripts": {
   "test": "jest",
   "start": "node server.js",
   "start:dev": "nodemon server.js"
 },
 …
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script, nodemon server.js, will automatically restart the node application when the file notices changes.&lt;/p&gt;

&lt;p&gt;Note: This program would not run without APIs in the index.js file.&lt;/p&gt;

&lt;p&gt;For this project, we will use the model, view, controller (MVC) architecture pattern by dividing the application logic into three parts: the Model, View, and Controller.&lt;/p&gt;

&lt;p&gt;Let’s create the server.&lt;/p&gt;

&lt;p&gt;Building the model&lt;br&gt;
Create a new directory in the project's root called models, and within this folder, create a new file named friends.model.js.&lt;/p&gt;

&lt;p&gt;Copy and paste the code below to create the model for our API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const friends = [];

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

&lt;/div&gt;



&lt;p&gt;This model contains an empty array with the declared variable, friends and exported variable.&lt;/p&gt;

&lt;p&gt;Building the controller&lt;br&gt;
For the controller, with the same process as the model, create a directory named controllers in the project’s root and within the folder, a new file, friends.controller.js.&lt;/p&gt;

&lt;p&gt;Copy-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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/friends.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFriends&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOneFriend&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="nx"&gt;friendId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;friendId&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;friend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;friendId&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;friend&lt;/span&gt;&lt;span class="p"&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;404&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Friend does not exist&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;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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;postNewFriend&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;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;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="nx"&gt;name&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;400&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing friend name&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newFriend&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;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="nx"&gt;name&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="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newFriend&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;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newFriend&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getFriends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getOneFriend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;postNewFriend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;*&lt;em&gt;Import the model *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the getFriends(), this returns the list of friends in json format &lt;br&gt;
The getOneFriend(), will return just one friend in the array using the id identifier, and if the id does not exist, it displays a client error, 404&lt;br&gt;
The postNewFriend() uses the post-request method to send the data to the server&lt;br&gt;
As usual, export the three functions with module.exports&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the route&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The API endpoint in this section will communicate with each of the controllers using its corresponding HTTP request.&lt;/p&gt;

&lt;p&gt;Create a new folder called routes with a new file, friends.router.js, with each router attached to the endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy and paste this code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');

const friendsController = require('../controllers/friends.controller');

const friendsRouter = express.Router();

friendsRouter.get('/', friendsController.getFriends);
friendsRouter.get('/:friendId', friendsController.getOneFriend);
friendsRouter.post('/', friendsController.postNewFriend);

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

&lt;/div&gt;



&lt;p&gt;The code above does this:&lt;br&gt;
shows how to create a new router object using the Router() function&lt;br&gt;
Import the friendsController and use them in the friendsRouter to handle the request&lt;br&gt;
Export the friendsRouter&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting it all&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most important of creating or building an API server is to connect the workflow so that the MVC pattern functions to test individual endpoints.&lt;/p&gt;

&lt;p&gt;First, create the entry point file, app.js, in the root directory and paste this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;friendsRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/friends.router&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="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="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;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&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;url&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;delta&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;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;welcome to the friends data response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/friends&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;friendsRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The highlights for the code above does the following:&lt;br&gt;
The express module is declared&lt;br&gt;
Run the express() function, as this defines the instance of the app&lt;br&gt;
The middleware, app.use() parses the incoming JSON requests. The other middleware with endpoint, /friends, is the route attached to the hostname of the app&lt;br&gt;
The GET request with the endpoint / displays the message in the res.send() method in the browser at &lt;a href="http://localhost:4000"&gt;http://localhost:4000&lt;/a&gt;&lt;br&gt;
Export the app in the module.exports class&lt;/p&gt;

&lt;p&gt;Next, create the server.js file in the root directory with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
const http = require('http');
const app = require('./app');

const server = http.createServer(app);

const PORT = 4000;

server.listen(PORT, () =&amp;gt; {
  console.log(`Server listening on port:${PORT}`);
});

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

&lt;/div&gt;



&lt;p&gt;The code above with the listen() function listen to the connections on the specified port and host.&lt;/p&gt;

&lt;p&gt;Let’s now run the server with the command:&lt;br&gt;
&lt;code&gt;npm run start:dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Checking the console of your terminal, it should read “Server listening on port:4000”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Postman to test the server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating Postman collections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before creating collections for your requests, you need to create a workspace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Click the Workspaces dropdown and select Create Workspace.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttng63lyyrj2hxebs7bo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fttng63lyyrj2hxebs7bo.png" alt="Image description" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your workspace a name and set the visibility to Public. Thereafter, click Create Workspace.&lt;/p&gt;

&lt;p&gt;Let’s create a new Collection in our workspace to store the requests. At the left pane of the Postman window, click the plus (“+”) icon or Create Collection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tdag74yifn5njv9e54q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tdag74yifn5njv9e54q.png" alt="Image description" width="612" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next, give the Collection a name.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adding Requests to Collections&lt;br&gt;
To create our first request, click either the Add a request inside your new collection or the three dots icon on your Collection&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwd2elljepehnp1ar9zz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwd2elljepehnp1ar9zz.png" alt="Image description" width="738" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, name your request “post friends”. Set the request method to POST and the request URL to &lt;a href="http://localhost:4000/friends"&gt;http://localhost:4000/friends&lt;/a&gt;, which will send data to the server to create a friend. Remember to hit Save.&lt;/p&gt;

&lt;p&gt;Click the Body tab of the request, select data type raw &amp;gt; JSON, and send the request by clicking the Send button.&lt;/p&gt;

&lt;p&gt;Enter a name as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw51n5puxdk6r0qrppdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw51n5puxdk6r0qrppdt.png" alt="Image description" width="780" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm the request with the GET request method, add a request named get friends with the path URL &lt;a href="http://localhost:4000/friends"&gt;http://localhost:4000/friends&lt;/a&gt; which will display the friends' array in the response field as shown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nwnhrvdcbspkkq5zgfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nwnhrvdcbspkkq5zgfr.png" alt="Image description" width="797" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get an individual friend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add a request “get a friend” to see the output of just one friend specifying the index included in the path URL, http:://localhost:4000/friends/0, with a GET request method.&lt;/p&gt;

&lt;p&gt;The result of this should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqyyxkth9pbc8saru8qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqyyxkth9pbc8saru8qa.png" alt="Image description" width="777" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating documentation for test APIs&lt;br&gt;
Documenting your API workflow is essential, just like how developers create a README.md file for every GitHub project they create. Similarly, it is possible to do the same in Postman.&lt;/p&gt;

&lt;p&gt;At the right pane of the app, Select the Documentation icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mfaggbovdxlsg9wa2vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mfaggbovdxlsg9wa2vy.png" alt="Image description" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once opened, you can write a detailed request description of each endpoint in your collection by clicking the pencil icon to begin on hover of the text&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbgyeuccgurcxarhiuh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbgyeuccgurcxarhiuh9.png" alt="Image description" width="792" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article taught you the basics of using Postman to test your APIs by sending requests over the network and receiving responses through the status code, indicating either success or failed error status code.&lt;/p&gt;

&lt;p&gt;Try Postman today and learn from its resources. If you found this article helpful, leave a comment.&lt;br&gt;
Further reading&lt;br&gt;
Postman API documentation&lt;br&gt;
Get started with Node.js&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Learning Vue.js: A React Developer's Journey Through a New Codebase</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 31 May 2023 10:20:51 +0000</pubDate>
      <link>https://dev.to/programequity/learning-vuejs-a-react-developers-journey-through-a-new-codebase-4b9f</link>
      <guid>https://dev.to/programequity/learning-vuejs-a-react-developers-journey-through-a-new-codebase-4b9f</guid>
      <description>&lt;p&gt;&lt;em&gt;- Mason Mei&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a web developer experienced with React, joining the Amplify team introduced me to the world of Vue.js. Amplify is an open-source app empowering users to fight climate change by selecting climate campaigns and representatives to support. In this post, I'll discuss my experience learning Vue.js and the challenges I faced transitioning from React.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vue.js Environment and Ecosystem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Setting up the development environment for Vue.js involves installing Node.js, npm, and using Vue CLI to create and manage projects. I familiarized myself with core concepts like components, directives, and lifecycle hooks. To effectively work with Amplify's codebase, I learned about Vue.js counterparts for state management and routing: Vuex and Vue Router.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Navigating a New Codebase: From React to Vue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Transitioning from React to Vue.js required adapting to new syntax, structure, and conventions. Key differences include template-based syntax, Vue.js directives, and single-file components. Despite these differences, similarities like component-based architecture and props eased the transition.&lt;br&gt;
Working on Amplify's codebase, I faced challenges like understanding existing structure, implementing features, and fixing issues. Overcoming these hurdles taught me valuable lessons and best practices for working with Vue.js. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vue Router and Navigation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vue Router is essential for managing in-app navigation. By configuring routes and adding navigation links, developers create user-friendly interfaces. Features like route parameters and nested routes can help manage complex routing scenarios. Understanding these concepts helps developers work more effectively with Vue.js codebases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vuex for State Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Efficient state management is crucial for web applications, and Vuex is the go-to solution for Vue.js. Vuex revolves around four main concepts: state, actions, mutations, and getters. By learning how Vuex operates, I gained insights into state management in Vue.js applications, allowing me to work more effectively with Amplify's codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My journey into Vue.js was filled with learning experiences, challenges, and valuable insights. As a React developer, adapting to Vue.js while working on Amplify's codebase was initially challenging. However, I discovered that Vue.js offers a powerful, flexible, and approachable framework for building web applications.&lt;/p&gt;

&lt;p&gt;I encourage you to explore Vue.js and take advantage of its benefits. Whether you're a beginner or experienced developer, Vue.js has much to offer. Share your experiences, contribute to the Vue.js community, and access resources like official documentation, courses, tutorials, and discussions. Happy coding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Implement a Full Stack Logging Solution for a Vue.js and Node.js App</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 31 May 2023 10:14:42 +0000</pubDate>
      <link>https://dev.to/programequity/how-to-implement-a-full-stack-logging-solution-for-a-vuejs-and-nodejs-app-5bj3</link>
      <guid>https://dev.to/programequity/how-to-implement-a-full-stack-logging-solution-for-a-vuejs-and-nodejs-app-5bj3</guid>
      <description>&lt;p&gt;&lt;em&gt;- Rachel Sensenig&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;JavaScript developers are familiar with&lt;code&gt;console.log()&lt;/code&gt;,which writes output to your browser  console. But what if you want to access logging information outside the browser console, say in a persisted data store like a file or database? Persisting logs can be incredibly helpful for understanding your application. It enables you to easily track activity, debug errors, and generally understand what happens and when in your application – all critical for maintaining a healthy, well-functioning app.&lt;/p&gt;

&lt;p&gt;As part of the Major League Hacking Fellowship, I was matched with working on Program Equity’s open source app Amplify. For this project, I implemented a full stack logging solution in production to address an intermittent race condition between a back-end operation completing, and a front-end page render. While there are libraries that can handle both back- and front-end logging, I opted to use separate libraries for my front-end and back-end loggers, specifically Winston, a logger for Node.js, for the back-end, and vue-logger-plugin for the front-end.&lt;/p&gt;

&lt;p&gt;This article will show you how to connect client and server-side loggers through an API endpoint and persist log messages from your Vue.js front-end in your Node.js back-end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating a Full Stack Logging Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Creating the API Endpoint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, create an API endpoint that will receive logging requests. I opted to use Express and &lt;code&gt;express. Router()&lt;/code&gt;.Since we’ll be writing to a data store, a creative act, you’ll want to make this a POST route. In the code examples below, this endpoint is &lt;code&gt;api/event_logger/log&lt;/code&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;// event_logger.js:&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&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="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;/log&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;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;// Destructure request body sent by client&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;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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="c1"&gt;// Send response back to client&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;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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


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

&lt;/div&gt;

&lt;p&gt;To test that your endpoint is working as expected, I recommend using Postman, a free developer tool that makes it easy to make API requests to any endpoint with any request body. First, we’ll test whether you can receive and send a log message. Create a JSON object with &lt;code&gt;{severity, data}&lt;/code&gt; as the “Body” in Postman with a logging level for severity (i.e. “info”) and a test message for data (i.e. “Hello World”). You should see a response of this JSON object when you select “Send” in Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnv669mup4e574tonfjw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnv669mup4e574tonfjw7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Creating the Backend Logger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we’ll build a back-end logger. For this case, we’ll use Winston because it is a non-blocking logging library that can log to anything from a plain text file to SASS services. This logger will have two modes, File and Console. Winston refers to these modes as “transports”. File transport will log to a file of your choice – in this case, &lt;code&gt;error.log&lt;/code&gt; – while Console transport will log to the browser console. We don’t want our end users to see random log messages in the console, so the Console transport will only be enabled in the development environment. You can adjust the format as needed for your project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;backend_logger&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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;winston&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errors&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;colorize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;printf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;stack&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="c1"&gt;// Specify the name of the file to write to here&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&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.log&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 log format for when we're in development&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;level&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="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&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;level&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;stack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// if we're not in production, then log to the console&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&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;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// add color depending on log level&lt;/span&gt;
        &lt;span class="nf"&gt;colorize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YYYY-MM-DD HH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="c1"&gt;// include stack-trace&lt;/span&gt;
        &lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nx"&gt;logFormat&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;defaultMeta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-service&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In your API endpoint, require the back-end logger that you created and start logging messages to it. In this case, since we’re using Winston, we’ll use &lt;code&gt;logger.info&lt;/code&gt; to log the message we’ve extracted from the request body. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;event_logger&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../utilities/backend_logger&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;/log&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;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;// Destructure request body sent by client&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;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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="c1"&gt;// Send response status of 200 back to client&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;sendStatus&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="c1"&gt;// send log messages to back-end logger&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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


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

&lt;/div&gt;

&lt;p&gt;Same as before, we can use Postman to test that this is working as expected. Send that same POST request from above. In Postman, you should see a response of “OK” because you’re sending back a response status of 200.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farmuopy4317tqm96g6io.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farmuopy4317tqm96g6io.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re in development, you should also see a message logged to the browser console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6ovhysz3w6gvahk7k0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr6ovhysz3w6gvahk7k0i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Creating the Frontend Logger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I discovered that Winston is difficult to use in a Vue.js front-end due to creating too many polyfills. As a result, you need a separate front-end logger, and a way for it to talk to the back-end.&lt;/p&gt;

&lt;p&gt;For the client-side logger, we’ll use &lt;code&gt;vue-logger-plugin&lt;/code&gt;. I like this library because it allows you to create custom hooks. In this case, we’ll build a custom after hook that leverages another library, axios, to send log messages to your API endpoint.&lt;/p&gt;

&lt;p&gt;Axios is a popular JavaScript library that allows you to send HTTP requests from a web browser or Node.js server. It provides an easy way to send asynchronous requests to the server, which means that the client code can continue running while the request is being sent and a response is being awaited. This is important for log messages, which are often sent in the background and shouldn't interrupt the main application logic.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;frontend_logger&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VueLogger&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;vue-logger-plugin&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;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// after hook to send log messages to api endpoint&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ServerLogHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;axios&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;/api/event_logger/log&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;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argumentArray&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;// define options&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;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;afterHooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ServerLogHook&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// export logger with applied options&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VueLogger&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we’ll make our new front-end logger available for use by attaching it to our application. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;main&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;vue&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;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// import front-end logging&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;vueLogger&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;./utilities/frontend_logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// for using front-end logger&lt;/span&gt;
&lt;span class="nx"&gt;Vue&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;vueLogger&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;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;h&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="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;$mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After it’s attached,  the &lt;code&gt;$log&lt;/code&gt; and &lt;code&gt;$logger&lt;/code&gt; properties will be available globally in your Vue app. An easy way to test is to input &lt;code&gt;this.$log&lt;/code&gt; or &lt;code&gt;this.$logger&lt;/code&gt; directly into your Vue instance, so you’ll see these test messages when your Vue app loads.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;main&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&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;testObject&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;this is a test object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// using $log&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&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 Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// using $logger&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$logger&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;Test Message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testObject&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;&lt;strong&gt;Step 4: Hooking it all together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that all the pieces are in place, let’s try this out. Fire up  your application in development. You should see the test log messages appear in the console and in the &lt;code&gt;error.log&lt;/code&gt; file, which we set in the Winston logger. You’re now free to add logging statements anywhere you please in your application on the frontend with &lt;code&gt;this.$log&lt;/code&gt; and &lt;code&gt;this.$logger&lt;/code&gt; and on the backend by including and invoking the Winston logger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In summary, you can create a full stack logging solution by creating an API endpoint that passes log messages from a front-end logger to a back-end logger. The back-end logger then saves log messages to a file and optionally prints to the console. For a Vue.js and Node.js application, I used vue-logger-plugin for the front-end and Winston for the back-end. Use Postman to test your API endpoint along the way.&lt;/p&gt;

&lt;p&gt;After the full stack logger is set up, you can customize according to your needs. The $log or $logger global properties can be used anywhere in your Vue app, including child components. With Winston, you can create additional custom transports to save information to your storage place of choice, such as a database.&lt;/p&gt;

&lt;p&gt;Try this out, and please leave a comment of what you create if you do!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Welcome First-Time Contributors to Your Repository Using GitHub Actions</title>
      <dc:creator>programequity</dc:creator>
      <pubDate>Wed, 31 May 2023 02:24:43 +0000</pubDate>
      <link>https://dev.to/programequity/how-to-welcome-first-time-contributors-to-your-repository-using-github-actions-390p</link>
      <guid>https://dev.to/programequity/how-to-welcome-first-time-contributors-to-your-repository-using-github-actions-390p</guid>
      <description>&lt;p&gt;&lt;em&gt;- Alexis Gonzales&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What is a Welcome Message Action?&lt;br&gt;
&lt;/u&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;A welcome message triggers a GitHub action to post a message whenever a new contributor opens their first issue or pull request in your repository (repo). Why do we want to consider using a welcome message? The one-worded answer that comes to mind is community. Community comes from my personal journey while contributing to open source with Amplify. Amplify’s goal was to create a welcoming environment for all open-source contributors. &lt;/p&gt;

&lt;p&gt;Since contributors are from all over the world, creating a welcome message is an easy but impactful step towards fostering a sense of community for your repository. Prior to Amplify, I had no idea what GitHub Actions was or how to even implement this, so let’s break them down together!                                                          &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What is GitHub Actions?&lt;br&gt;
&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
    GitHub Actions is a platform that allows developers to integrate continuous integration and continuous delivery to automate their build, test, and deployment pipeline. GitHub Actions does more than devOps, it lets developers run workflows when events happen in your repository. Let's dive into running workflows in more detail!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;What is a GitHub Marketplace&lt;br&gt;
&lt;/u&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;So far we have covered what GitHub Actions is, but how are actions created? We could build a welcome message but there is no need to! GitHub Marketplace has one that we can use! GitHub Marketplace is where you can discover, browse, and install free and paid tools, including GitHub Apps, OAuth Apps, and GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;How to create Welcome Message&lt;br&gt;
&lt;/u&gt;&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;In order to create our welcome message, we will need to use an action called “welcome-new-contributors”, to read the official documentation for this bot click here. To add the welcome bot, we will need to create a YAML file to configure the automated process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure Permissions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The bot will need to have read and write access to the repo. This bot will act like a contributor on your repo. To accomplish adding your welcome bot as a contributor, follow the steps below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Create a new GitHub account to be used as the bot’s account.&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Go to your repository and give the bot’s account access to be a collaborator on your repository that you want the workflow to execute. Ensure the bot has permission to write on the project’s repository.&lt;/p&gt;

&lt;p&gt;Next, create a new token on our bot’s GitHub account by going to Settings &amp;gt; Developer settings &amp;gt; Personal access tokens so the workflow action can use your bot account to comment on issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m1yfam6wb55yauevi7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m1yfam6wb55yauevi7n.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmj7rf271v7yn8ydhv44i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmj7rf271v7yn8ydhv44i.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8vkhgoiwty25t4sykfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8vkhgoiwty25t4sykfg.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5x2vwqnssmk9h7e5w3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5x2vwqnssmk9h7e5w3f.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You did it! You have connected your welcome bot account to your project repo. Let’s create a Workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Configure a Workflow YAML&lt;/strong&gt;&lt;br&gt;
&lt;u&gt;What is a YAML?&lt;/u&gt;&lt;br&gt;
The purpose of YAML is to create the road map of when we want our workflow to execute. Workflows are defined in the .github/workflows directory of the repository. Let’s review some definitions that way we understand the building blocks needed. There are more components when it comes to coding a YAML but we will focus on the terms that we will use to construct our YAML. For a more in-depth explanation of GitHub Actions head to the GitHub Docs here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7l1jaxp1y1oms2vn90c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7l1jaxp1y1oms2vn90c.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure YAML&lt;/strong&gt;&lt;br&gt;
In your project repository create the following directory “.github/workflows”. Within the workflow directory, create a “welcome_action.YAML” and then paste the code below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML Template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: 'Welcome New Contributors'

on:
  issues:
    types: [opened]
  pull_request_target:
    types: [opened]

jobs:
  welcome-new-contributor:
    runs-on: ubuntu-latest
    steps:
      - name: 'Greet the contributor'
        uses: garg3133/welcome-new-contributors@v1.2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          issue-message: 'Hello there, thanks for opening your first issue. We welcome you to the community!'
          pr-message: 'Hello there, thanks for opening your first Pull Request. Someone will review it soon.'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Want to tag the contributor in the message?&lt;/strong&gt;&lt;br&gt;
Use @contributor_name anywhere in the message and it will be automatically replaced by the contributor's username.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt; &lt;br&gt;
If you made it all the way to the end of the article, you can be proud of yourself for creating a welcome message on the first PR and Issues opened by a contributor. I encourage you to use this welcome bot to create a sense of community while also increasing communication. &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
