<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jingjie&#39;s Blog</title>
  
  
  <link href="https://www.jingjies-blog.com/atom.xml" rel="self"/>
  
  <link href="https://www.jingjies-blog.com/"/>
  <updated>2025-01-02T10:34:37.000Z</updated>
  <id>https://www.jingjies-blog.com/</id>
  
  <author>
    <name>Jingjie Jiang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>DevOps for Backend Engineer I - What You Should Know about Docker</title>
    <link href="https://www.jingjies-blog.com/2024/12/23/what-you-should-know-about-docker-I/"/>
    <id>https://www.jingjies-blog.com/2024/12/23/what-you-should-know-about-docker-I/</id>
    <published>2024-12-23T19:51:28.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Nowadays, using Docker with Microservices is common practice, as it brings isolation, portability, scalability, and more. It will be a surprising for a backend engineer &#x2F; full-stack engineer not to familiar with Docker (of course, combined with Kubernetes). In the article, I will introduce the core concept of Docker from the practical perspective, whether you are a college student who are interested in leraning Docker or a junior software developer just beginning your career in the software industry, you should be able to grasp the essentils of Docker that you need in practice in 10 minutes by reading the article.</p><h2 id="The-workflow-with-Docker-in-industrial-projects"><a href="#The-workflow-with-Docker-in-industrial-projects" class="headerlink" title="The workflow with Docker in industrial projects"></a>The workflow with Docker in industrial projects</h2><p>I found the following chart that perfectly explain the workflow with Docker in practice <a href="https://www.youtube.com/watch?v=3c-iBn73dDE">Docker Tutorial for Beginners [FULL COURSE in 3 Hours]</a>.</p><figure>  <img src="https://i.imgur.com/pHSuZQB.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Fig.1 - The Workflow with Docker </b>  </figcaption></figure><p>As shown above, the steps of the workflow are (all Docker related terms will be explained in the next section):</p><ul><li>After you complete the application development and commit the code to Git, Jenkins will be notified via webhooks</li></ul><ul><li>Jenkins will buid the docker images according to the dockerfile provide</li><li>Jenkins will <strong>push</strong> the built images to the Docker repository, like Docker Hub or AWS ECR (Elastic Container Registry)</li><li>The server will <strong>pull</strong> the images from the Docker repository, then create and run containers according to the Docker Compose files (<em>*compose.yaml</em> files)</li></ul><h2 id="The-Core-Concepts-of-Docker"><a href="#The-Core-Concepts-of-Docker" class="headerlink" title="The Core Concepts of Docker"></a>The Core Concepts of Docker</h2><p>In the above workflow, I mentioned many essetianls in Docker. In this section, I will explain them in details.</p><h3 id="Docker-Sever-Docker-Deamon"><a href="#Docker-Sever-Docker-Deamon" class="headerlink" title="Docker Sever (Docker Deamon)"></a>Docker Sever (Docker Deamon)</h3><p>It’s responsible for managing Docker containers, images, networks, and volumes. It is run on the same machine where Docker is installed. The Docker Client will send all requests to Docker Server.</p><h3 id="Docker-Client-CLI"><a href="#Docker-Client-CLI" class="headerlink" title="Docker Client (CLI)"></a>Docker Client (CLI)</h3><p>The Docker Client is the interface that users interact with when using Docker.</p><h3 id="Docker-Hub"><a href="#Docker-Hub" class="headerlink" title="Docker Hub"></a>Docker Hub</h3><p>Docker Hub is a Docker Repository. It is the default public registry for Docker images, created and maintained by Docker, Inc.</p><h3 id="Docker-Container"><a href="#Docker-Container" class="headerlink" title="Docker Container"></a>Docker Container</h3><p>An instance of an image is called a container. It is the running image.</p><h3 id="Docker-Image"><a href="#Docker-Image" class="headerlink" title="Docker Image"></a>Docker Image</h3><p>Stopped container. Docker image are read-only layers, and the docker container is the writable layer on top of a image.</p><h3 id="Docker-Compose"><a href="#Docker-Compose" class="headerlink" title="Docker Compose"></a>Docker Compose</h3><p>Docker Compose is a powerful tool for defining and managing multi-container Docker applicaitons. In practice, you can sepcify your applicaton’s services, networks, and volumes in a docker-compose.yml file, and then use a single command to start<br>them all.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3.9&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">mongodb:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">mongo:6.0</span> <span class="comment"># Replace with the desired MongoDB version</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">mongodb-container</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;27017:27017&#x27;</span> <span class="comment"># Expose MongoDB&#x27;s default port</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="string">admin</span> <span class="comment"># MongoDB root username</span></span><br><span class="line">      <span class="attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="string">password</span> <span class="comment"># MongoDB root password</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">mongodb_data:/data/db</span> <span class="comment"># Persistent data storage</span></span><br><span class="line"></span><br><span class="line"><span class="attr">volumes:</span></span><br><span class="line">  <span class="attr">mongodb_data:</span></span><br><span class="line">    <span class="attr">driver:</span> <span class="string">local</span></span><br></pre></td></tr></table></figure><h3 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h3><p>A Dockerfile is a script that contains a series of instructions to build a Docker image, inccluding the base image,<br>dependencies, configurations, and commands to run the application.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Use the official Node.js LTS image as the base image</span></span><br><span class="line"><span class="string">FROM</span> <span class="string">node:18-alpine</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Set the working directory inside the container</span></span><br><span class="line"><span class="string">WORKDIR</span> <span class="string">/app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Copy package.json and package-lock.json files to the container</span></span><br><span class="line"><span class="string">COPY</span> <span class="string">package*.json</span> <span class="string">./</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Install dependencies</span></span><br><span class="line"><span class="string">RUN</span> <span class="string">npm</span> <span class="string">install</span> <span class="string">--production</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Copy the rest of the application files</span></span><br><span class="line"><span class="string">COPY</span> <span class="string">.</span> <span class="string">.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Expose the port the app runs on</span></span><br><span class="line"><span class="string">EXPOSE</span> <span class="number">3000</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Define the command to run the application</span></span><br><span class="line"><span class="string">CMD</span> [<span class="string">&quot;npm&quot;</span>, <span class="string">&quot;start&quot;</span>]</span><br></pre></td></tr></table></figure><p>About <code>CMD [&quot;npm&quot;, &quot;start&quot;]</code>: Specifies the command to start the Node.js application. Make sure your package.json has a start script defined (e.g., “start”: “node index.js”).</p><h3 id="Private-Docker-Registry"><a href="#Private-Docker-Registry" class="headerlink" title="Private Docker Registry"></a>Private Docker Registry</h3><p>Normally, we push our Docker images to pricate docker registry rather than a public one. A commonly used private Docker registry is AWS ERC (Elastic Container Registry).</p><h3 id="Docker-Volumes"><a href="#Docker-Volumes" class="headerlink" title="Docker Volumes"></a>Docker Volumes</h3><p>Docker Volumes is used to persist data in Docker and prevent data loss in stateful applications. They allow folders from the host’ physical file system (e.g. &#x2F;home&#x2F;mount&#x2F;data) to be mounted into the virtual file system (e.g. &#x2F;var&#x2F;lib&#x2F;mysql&#x2F;data) of Docker.</p><p>There are three types of Docker volumes:</p><ul><li>Docker-managed volumes: Managed entirely by Docker and stored in its default location.</li><li>Anonymous volumes: Automatically created without a specific name, typically for temporary use.</li><li>Named volumes: Explicitly named and can be referenced by their names. Named volumes are the most common choice in production environments due to their reusability and ease of management.</li></ul><h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>The above covers the core concepts of Docker, providing a quick start for beginners. You can explore each part in greater details based on your interests.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Nowadays, using Docker with Microservices is common practice, as it brings isolation, portability, scalability, and more. It will be a su</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Docker" scheme="https://www.jingjies-blog.com/categories/Technology/Docker/"/>
    
    <category term="Backend" scheme="https://www.jingjies-blog.com/categories/Backend/"/>
    
    <category term="DevOps" scheme="https://www.jingjies-blog.com/categories/Backend/DevOps/"/>
    
    
    <category term="Docker" scheme="https://www.jingjies-blog.com/tags/Docker/"/>
    
    <category term="DevOps" scheme="https://www.jingjies-blog.com/tags/DevOps/"/>
    
    <category term="Backend Software Development" scheme="https://www.jingjies-blog.com/tags/Backend-Software-Development/"/>
    
  </entry>
  
  <entry>
    <title>Promise.all in Practice</title>
    <link href="https://www.jingjies-blog.com/2024/06/17/Promise-all-in-practice/"/>
    <id>https://www.jingjies-blog.com/2024/06/17/Promise-all-in-practice/</id>
    <published>2024-06-17T22:27:24.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>There are tons of articles online that offer tutorials about how to use Promise.all. I will not reiterate those. In this article, I aim to brief summarise the use of Promise.all in real-world projects for those programmers transitioning from junior to mid-level.</p><h2 id="What-do-we-need-to-keep-in-mind-when-considering-using-Promise-all"><a href="#What-do-we-need-to-keep-in-mind-when-considering-using-Promise-all" class="headerlink" title="What do we need to keep in mind when considering using Promise.all"></a>What do we need to keep in mind when considering using Promise.all</h2><ul><li>Order matters. <strong>Promise.all</strong> returns the results in the same order as the source.</li><li>Fail one, Fail all. If one of the promise rejects, then the <strong>Promise.all</strong> will not wait for the remaining promise to execute but rejects right away.</li></ul><h2 id="When-do-we-need-Promise-all"><a href="#When-do-we-need-Promise-all" class="headerlink" title="When do we need Promise.all"></a>When do we need Promise.all</h2><p>In real project, normally when we have an array of data, for each element in the array, we need to do some async&#x2F;await operations, this is where we need <strong>Promise.all</strong>. For example, in the following code snippets from a real project, we have an array called <code>idsForDelete</code>, for each id in this array, we need to call an async method <code>removeMonitorTask(apiKey, id)</code>. Therefore, we use <strong>Promise.all</strong> to do async&#x2F;await calls on all ids in the array.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">const</span> deleteIds = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>(</span><br><span class="line">  idsForDelete?.<span class="title function_">map</span>(<span class="keyword">async</span> (id) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">removeMonitorTask</span>(apiKey, id);</span><br><span class="line">    <span class="keyword">return</span> response?.<span class="property">data</span>[<span class="number">0</span>]?.<span class="property">trackerId</span>?.<span class="title function_">toString</span>() === id.<span class="title function_">toString</span>();</span><br><span class="line">  &#125;)</span><br><span class="line">...</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h2 id="How-do-we-handle-exceptions-with-Promise-all"><a href="#How-do-we-handle-exceptions-with-Promise-all" class="headerlink" title="How do we handle exceptions with Promise.all"></a>How do we handle exceptions with Promise.all</h2><p>There are two ways of handling exceptions with <strong>Promise.all</strong>:</p><ul><li>catch an exception at the end of <strong>Promise.all</strong>;</li><li>catch an exception in each individual promise.</li></ul><p>Only catching exceptions in the latter way when we need to:</p><ul><li>Keep the results of other promise</li><li>Want to know the reason for each rejected promise</li></ul><p>Check the following example:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">p1 = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">10</span>);</span><br><span class="line">p2 = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">20</span>);</span><br><span class="line">p3 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) &#123;</span><br><span class="line">  <span class="built_in">setTimeout</span>(reject, <span class="number">100</span>, <span class="string">&#x27;test&#x27;</span>);</span><br><span class="line">&#125;).<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`internal err: <span class="subst">$&#123;err&#125;</span>`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([p2, p3, p1]);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(res);</span><br><span class="line">&#125;)().<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">`err: <span class="subst">$&#123;err&#125;</span>`</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>Execution result:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-- internal err: <span class="built_in">test</span></span><br><span class="line">-- [ 20, undefined, 10 ]</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>As we can see from the execution result that even <em>p3</em> is rejected, we still get the results of previous <em>p1</em> and the <em>p2</em> afterwards.</p><p>What if we only catch the exception after <strong>Promise.all</strong>?</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">p1 = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">10</span>);</span><br><span class="line">p2 = <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">20</span>);</span><br><span class="line">p3 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span> (<span class="params">resolve, reject</span>) &#123;</span><br><span class="line">  <span class="built_in">setTimeout</span>(reject, <span class="number">100</span>, <span class="string">&#x27;test&#x27;</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="title class_">Promise</span>.<span class="title function_">all</span>([p2, p3, p1]);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(res);</span><br><span class="line">&#125;)().<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">`err: <span class="subst">$&#123;err&#125;</span>`</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>Result:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-- err: <span class="built_in">test</span></span><br></pre></td></tr></table></figure><p>As shown above, <strong>Promise.all</strong> rejects all, we are not able to see the result of <em>p2</em>.</p><p>That is it, that is all you need to know about <strong>Promise.all</strong> in most of the time when developing real-world projects.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;There are tons of articles online that offer tutorials about how to use Promise.all. I will not reiterate those. In this article, I aim t</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Engineering" scheme="https://www.jingjies-blog.com/categories/Software-Engineering/"/>
    
    <category term="Programming Language" scheme="https://www.jingjies-blog.com/categories/Software-Engineering/Programming-Language/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Programming Language" scheme="https://www.jingjies-blog.com/tags/Programming-Language/"/>
    
  </entry>
  
  <entry>
    <title>The Re-render Issue with the Tabs Navigation in Expo Router</title>
    <link href="https://www.jingjies-blog.com/2024/04/17/re-render-issue-with-expo-router-tabs-navigation/"/>
    <id>https://www.jingjies-blog.com/2024/04/17/re-render-issue-with-expo-router-tabs-navigation/</id>
    <published>2024-04-17T18:58:15.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Last week I was working on a task of adding multiple language support to the React Native app that is under developing. I used the package <a href="https://www.npmjs.com/package/i18next">i18next</a> as the backhone to support the feature. <a href="https://www.npmjs.com/package/react-redux">React Redux</a> is also used to store the language choice as one of the app settings. During the development, I encountered an unexpected bug: The home screen does not re-render once I changed the language setting. It took me around 2.5 hours to resolve it, hence, I consider it worth a blog to write the think process and the solution down.</p><h2 id="Context-of-the-Issue"><a href="#Context-of-the-Issue" class="headerlink" title="Context of the Issue"></a>Context of the Issue</h2><p>First, let us go through the context of the issue.</p><h3 id="Store-the-Language-Settings-in-React-Redux"><a href="#Store-the-Language-Settings-in-React-Redux" class="headerlink" title="Store the Language Settings in React Redux"></a>Store the Language Settings in React Redux</h3><p>In the <strong><em>i18next</em></strong>, it has a global variable <code>i18n.language</code> to store language choice and is applied to the whole app. However, it is not enough, if we do not store the language setting, once the app is closed, the user choice will lost. Therefore, I used <strong><em>React Redux</em></strong> to store the language setting, as shown below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">const</span> initialState = &#123;</span><br><span class="line">  <span class="attr">language</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> appSettingSlice = <span class="title function_">createSlice</span>(&#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;appSettings&#x27;</span>,</span><br><span class="line">  initialState,</span><br><span class="line">  <span class="attr">reducers</span>: &#123;</span><br><span class="line">    <span class="attr">getLanguageSetting</span>: <span class="function">(<span class="params">state</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> state.<span class="property">language</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">setLanguageSetting</span>: <span class="function">(<span class="params">state, action</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (!action.<span class="property">payload</span>) <span class="keyword">return</span></span><br><span class="line">      state.<span class="property">language</span> = action.<span class="property">payload</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">resetLanguageSetting</span>: <span class="function">(<span class="params">state</span>) =&gt;</span> &#123;</span><br><span class="line">      state.<span class="property">language</span> = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="The-Expected-Scenario"><a href="#The-Expected-Scenario" class="headerlink" title="The Expected Scenario"></a>The Expected Scenario</h3><p>The project is structured as blow. The <code>language</code> in redux is set under <em>set-language&#x2F;index.jsx</em>, along with <code>i18n.language</code>. And <em>set-language</em> is routed from <em>my-account</em>. What I expected is that if I change the <code>language</code>, once I switch back to the <em>home</em> screen, it should re-render and present with the changed language.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">-- root</span><br><span class="line">  -- app</span><br><span class="line">    -- (tabs)</span><br><span class="line">      -- home</span><br><span class="line">        ...</span><br><span class="line">        -- index.jsx</span><br><span class="line">        ...</span><br><span class="line">      -- my-account</span><br><span class="line">      ...</span><br><span class="line">    -- set-language</span><br><span class="line">      -- index.jsx</span><br><span class="line">      -- index.style.js</span><br><span class="line">    ...</span><br><span class="line">    -- index.js // entry</span><br><span class="line">    -- _layout.js</span><br><span class="line">    ...</span><br><span class="line">  ...</span><br><span class="line">-- index.js</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="The-Re-render-Issue"><a href="#The-Re-render-Issue" class="headerlink" title="The Re-render Issue"></a>The Re-render Issue</h3><p>Things did no go as I expected. Once I change the language setting and switch back to the <em>home</em> screen, it changes nothing, the scren still show the preivous language.</p><h2 id="RCA-Root-Cause-Analysis"><a href="#RCA-Root-Cause-Analysis" class="headerlink" title="RCA (Root Cause Analysis)"></a>RCA (Root Cause Analysis)</h2><p>I added a <code>useEffct</code> hook into <em>(tabs)&#x2F;home&#x2F;index.jsx</em>, and would like to see why the <em>home</em> screen does not re-render. The code is shown below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`language: <span class="subst">$&#123;language&#125;</span>`</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`i18n.language: <span class="subst">$&#123;i18n.language&#125;</span>`</span>);</span><br><span class="line">  i18next.<span class="title function_">changeLanguage</span>(i18n.<span class="property">language</span>);</span><br><span class="line">&#125;, []);</span><br></pre></td></tr></table></figure><p>Then I changed the <code>language</code> and <code>i18n.language</code> from the <em>set-language</em> again, I found out that as soon as I changed the variables, the logs were shown on the terminal (means that the <code>useEffect</code> hook in the <em>home</em> screen was triggered). When I switched back to the <em>home</em> screen, the terminal did not print out the logs again (means that <code>useEffect</code> hook was not trigered).</p><p>Hence, I reached to the conclusion that under the tabs navigation of Expo Router, the tabs are somehow binded together, i.e. change states in one tab, will cause the <code>useEffect</code> hooks in the other tabs triggered. In this case, the other tabs consider themselves are re-rendered already. For example, in my case, I changed the <code>language</code> state under <em>my-account&#x2F;set-language</em>, the <code>useEffect</code> hook in the <em>home</em> screen is triggerd, since I am still in the <em>set-language</em> screen, the components in the <em>home</em> screen will not be re-endered. When I switch back to the <em>home</em> screen, as there is no state change, the <code>useEffect</code> hook will not be triggered, and the language in the <em>home</em> screen stay with the old one.</p><h2 id="The-Solution"><a href="#The-Solution" class="headerlink" title="The Solution"></a>The Solution</h2><p>After identifying the root cause, the thought for the solution is obvious: find a method to ‘force’ the <em>(tabs)&#x2F;home</em> screen to be re-rendered when change the language state in the <em>(tabs)&#x2F;my-account&#x2F;set-language</em> screen.</p><p>Enlightened by the information provided from <a href="https://github.com/jaredh159/tailwind-react-native-classnames/issues/234">Device context doesn’t work with Expo Router</a>, I use the <code>i18n.language</code> as the <em>key</em> value for the <code>Stack</code> component under <em>app&#x2F;_layout.js</em> (which is the parent componet for <em>(tabs)</em> ). Therefore, everytime the <code>i18n.language</code> is changed, the <em>key</em> value of the <code>Stack</code> component of <em>app&#x2F;_layout.js</em> will be changed, thus cause all tabs under the <em>(tabs)</em> directory be re-rendered. The source code is shown below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Layout</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; i18n &#125; = <span class="title function_">useTranslation</span>()</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">Provider</span> <span class="attr">store</span>=<span class="string">&#123;store&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">PersistGate</span> <span class="attr">loading</span>=<span class="string">&#123;null&#125;</span> <span class="attr">persistor</span>=<span class="string">&#123;persistor&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">Stack</span> <span class="attr">key</span>=<span class="string">&#123;JSON.stringify(i18n?.language)&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">Stack.Screen</span> <span class="attr">name</span>=<span class="string">&#x27;(tabs)&#x27;</span> <span class="attr">options</span>=<span class="string">&#123;&#123;</span> <span class="attr">headerShown:</span> <span class="attr">false</span> &#125;&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">Stack</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">PersistGate</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">Provider</span>&gt;</span></span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Compile and run the app again. Bingo! The feature works as expcted :)</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Last week I was working on a task of adding multiple language support to the React Native app that is under developing. I used the packag</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="React Native" scheme="https://www.jingjies-blog.com/categories/Technology/React-Native/"/>
    
    <category term="Expo" scheme="https://www.jingjies-blog.com/categories/Technology/React-Native/Expo/"/>
    
    <category term="React Native" scheme="https://www.jingjies-blog.com/categories/React-Native/"/>
    
    <category term="Expo Router" scheme="https://www.jingjies-blog.com/categories/React-Native/Expo-Router/"/>
    
    <category term="React Redux" scheme="https://www.jingjies-blog.com/categories/React-Native/Expo-Router/React-Redux/"/>
    
    
    <category term="React Native" scheme="https://www.jingjies-blog.com/tags/React-Native/"/>
    
    <category term="Expo" scheme="https://www.jingjies-blog.com/tags/Expo/"/>
    
    <category term="Expo Router" scheme="https://www.jingjies-blog.com/tags/Expo-Router/"/>
    
  </entry>
  
  <entry>
    <title>Integrate Redux Into Expo App with the Tabs Navigation Structure - Without the App Component</title>
    <link href="https://www.jingjies-blog.com/2024/04/01/integrate-redux-into-expo-with-tabs-navigation/"/>
    <id>https://www.jingjies-blog.com/2024/04/01/integrate-redux-into-expo-with-tabs-navigation/</id>
    <published>2024-04-01T22:42:43.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>In the last few months, I am working on a Defi app which is based on the <a href="https://expo.dev/">Expo</a> platform. One of the ‘must have’ thing is that the app need to store insensitive user data locally, such as app settings. After comparing with several potential solutions, like <a href="https://github.com/mrousavy/react-native-mmkv#readme">react-native-mmkv</a> (Not compatible with Expo), <a href="https://docs.expo.dev/versions/latest/sdk/sqlite/">expo-sqlite</a> (Not compatiblw with the Expo for web), <a href="https://www.npmjs.com/package/redux-persist">redux-persist</a> (build on top of Async Storage, compatible with Android, iOS and web, easy to use), I decided to adopt Redux.</p><p>I searched a bit regarding integrating Redux into Expo, however, in my Expo project, I used tabs navigation structure, so there is no app.js as the main entry (The file structure is listed blow). And all the solutions that I found only works on the Expo project that has an App component (normally app.js) as an entry point. After a few hours of trouble shooting, I finally found the solution myself and decided to share the method in this article, hopefully it will help someone in the future.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">-- root</span><br><span class="line">  -- app</span><br><span class="line">    -- (tabs)</span><br><span class="line">      -- home</span><br><span class="line">      ...</span><br><span class="line">    -- index.<span class="property">js</span> <span class="comment">// entry</span></span><br><span class="line">    -- _layout.<span class="property">js</span></span><br><span class="line">    ...</span><br><span class="line">  ...</span><br><span class="line">-- index.<span class="property">js</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><h2 id="Step-by-Step-Guide"><a href="#Step-by-Step-Guide" class="headerlink" title="Step by Step Guide"></a>Step by Step Guide</h2><p>Although the way to the destination is rugged, the solution is quite simple. To embed Redux into an Expo project with the tabs navigation structure, we only need to add the redux provider into two files: app&#x2F;index.js and app&#x2F;_lauout.js.</p><h3 id="app-x2F-index-js"><a href="#app-x2F-index-js" class="headerlink" title="app&#x2F;index.js"></a>app&#x2F;index.js</h3><p>As shown below, we wrap the <code>&lt;Redirect href=&quot;/home&quot; /&gt;</code> with the Redux provider and the PersistGate.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; useRootNavigationState, <span class="title class_">Redirect</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;expo-router&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Provider</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;react-redux&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PersistGate</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;redux-persist/es/integration/react&#x27;</span>;</span><br><span class="line"><span class="comment">// store and persistor are self-defined common redux component, nothing special</span></span><br><span class="line"><span class="keyword">import</span> &#123; store, persistor &#125; <span class="keyword">from</span> <span class="string">&#x27;../services/redux/store&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Home</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">Provider</span> <span class="attr">store</span>=<span class="string">&#123;store&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">PersistGate</span> <span class="attr">loading</span>=<span class="string">&#123;null&#125;</span> <span class="attr">persistor</span>=<span class="string">&#123;persistor&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">Redirect</span> <span class="attr">href</span>=<span class="string">&quot;/home&quot;</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">PersistGate</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">Provider</span>&gt;</span></span></span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Home</span>;</span><br></pre></td></tr></table></figure><h3 id="app-x2F-layout-js"><a href="#app-x2F-layout-js" class="headerlink" title="app&#x2F;_layout.js"></a>app&#x2F;_layout.js</h3><p>This is the tricky part, I could not image that to make Redux work, I need to wrap the Stack with the Redux provider and the PersistGate until I found out that this is a key step.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Stack</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;expo-router&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">Provider</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;react-redux&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PersistGate</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;redux-persist/es/integration/react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; store, persistor &#125; <span class="keyword">from</span> <span class="string">&#x27;../services/redux/store&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Layout</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">Provider</span> <span class="attr">store</span>=<span class="string">&#123;store&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;<span class="name">PersistGate</span> <span class="attr">loading</span>=<span class="string">&#123;null&#125;</span> <span class="attr">persistor</span>=<span class="string">&#123;persistor&#125;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">Stack</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">          <span class="tag">&lt;<span class="name">Stack.Screen</span> <span class="attr">name</span>=<span class="string">&quot;(tabs)&quot;</span> <span class="attr">options</span>=<span class="string">&#123;&#123;</span> <span class="attr">headerShown:</span> <span class="attr">false</span> &#125;&#125; /&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;/<span class="name">Stack</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">      <span class="tag">&lt;/<span class="name">PersistGate</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">Provider</span>&gt;</span></span></span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Layout</span>;</span><br></pre></td></tr></table></figure><p>After adding the two, I am able to use the Redux in the files under <code>/app</code>. Happy coding: )</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;In the last few months, I am working on a Defi app which is based on the &lt;a href=&quot;https://expo.dev/&quot;&gt;Expo&lt;/a&gt; platform. One of the ‘must </summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="React Native" scheme="https://www.jingjies-blog.com/categories/Technology/React-Native/"/>
    
    <category term="Expo" scheme="https://www.jingjies-blog.com/categories/Technology/React-Native/Expo/"/>
    
    
    <category term="React Native" scheme="https://www.jingjies-blog.com/tags/React-Native/"/>
    
    <category term="Expo" scheme="https://www.jingjies-blog.com/tags/Expo/"/>
    
  </entry>
  
  <entry>
    <title>Avoid Overengineering - A Case Study of Simplied System Design Related to Message Queue</title>
    <link href="https://www.jingjies-blog.com/2023/08/02/avoid-over-engineering-a-case-study/"/>
    <id>https://www.jingjies-blog.com/2023/08/02/avoid-over-engineering-a-case-study/</id>
    <published>2023-08-02T20:14:59.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Overengineering is a word that a software engineer will hear about from time to time through his&#x2F;her whole career. I recalled a design case that I encountered a few months back that perfectly explain what is overengineering in practice.</p><h2 id="The-Definition-of-Overengineering"><a href="#The-Definition-of-Overengineering" class="headerlink" title="The Definition of Overengineering"></a>The Definition of Overengineering</h2><p>On Wikipedia, <a href="https://hexo.io/docs/">Overengineering</a> is defined as “the act of designing a product or providing a solution to a problem in an elaborate or complicated manner, where a simpler solution can be demonstrated to exist with the same efficiency and effectiveness as that of the original design.” I would like to simplify it as: choose a more complicated solution to resolve a problem when there exist simpler ones.</p><h3 id="How-to-avoid-overengineering"><a href="#How-to-avoid-overengineering" class="headerlink" title="How to avoid overengineering"></a>How to avoid overengineering</h3><p>The tricky thing is, in practice, normally it is hard to identify overengineering in the system that is designed by yourself.</p><p>From my own personal experience, except harness yourself with rich knowledge and hands on experiences, the best option is to leverage collective wisdom, i.e. consult as many people as you can.</p><h2 id="Case-Study"><a href="#Case-Study" class="headerlink" title="Case Study"></a>Case Study</h2><p>A few months ago, my team had a design decision to make for a web system that is under developing. The scenario can be simplified as:</p><ul><li>A group of users, on peak time, the QPS was estimated as around 5K, would request and compete for a limited number of resources (the number of the resources would be less than 500) in a pool.</li><li>Users would request the resources via RestAPI call. The users’ requests would be served in First Come First Serve style, non pre-emptive.</li><li>The individual resource itself has a rate limiting, if it receives more than 20 requests per minute, then it will return an error and become unavailable for a few minutes.</li><li>VIP users could be assigned a resource exclusively and still under the “20 requests per minute” rule.</li><li>According to internal tests (from the ones who were not from the dev team), the possibility that requests from the user might take more than 3 seconds to finish was high (roughly more then 98%).</li></ul><p>The design is targeted for resolving two issues of the scenarios:</p><h4 id="Resources-competition"><a href="#Resources-competition" class="headerlink" title="Resources competition."></a>Resources competition.</h4><p>How should the system handle the case that multiple users competing for the limited number of resources?</p><h4 id="A-single-resource-received-more-then-20-requests-per-minute"><a href="#A-single-resource-received-more-then-20-requests-per-minute" class="headerlink" title="A single resource received more then 20 requests per minute."></a>A single resource received more then 20 requests per minute.</h4><p>How should the system handle the situation that a resource receives more then 20 requests per minute from a VIP user?</p><p>One of my colleague instantly proposed using message queues as the core of the solution. To be specific:</p><h4 id="for-“Resources-Competition”"><a href="#for-“Resources-Competition”" class="headerlink" title="for “Resources Competition”"></a>for “Resources Competition”</h4><ul><li>All the user requests should be put into a message queue and be pulled out in FIFO style.</li><li>A worker would pick a request and assign a resource once there is any available ones in the pool.</li></ul><h4 id="for-“More-than-20-Requests-issue”"><a href="#for-“More-than-20-Requests-issue”" class="headerlink" title="for “More than 20 Requests issue”"></a>for “More than 20 Requests issue”</h4><ul><li>Assign a message queue for each resource, store the requests sent from the user into a message queue, a worker pull requests from the queue in FIFO style.</li><li>Only let the requests be pulled out from the queue under the rate of 20 per minute.</li></ul><p>The solution is shown below.</p><figure>  <img src="https://i.imgur.com/uATqfJK.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Fig.1 - MQ Solution </b>  </figcaption></figure><h1 id="Add-graph"><a href="#Add-graph" class="headerlink" title="Add graph"></a>Add graph</h1><p>Everything seems perfectly “reasonable” right now. However, in the evening of the same day, I gave the problem a second thought, then I realised that message queues might be an overengineering solution.</p><h4 id="Resources-Competition"><a href="#Resources-Competition" class="headerlink" title="Resources Competition"></a>Resources Competition</h4><h5 id="From-User-Experiences-Perspective"><a href="#From-User-Experiences-Perspective" class="headerlink" title="From User Experiences Perspective"></a>From User Experiences Perspective</h5><ul><li>Resources Competition. On the one hand, let us assume if a message queue is not used here, then once all the resources are assigned, a user will receive an error for the resource request and the user will be informed by a message that he&#x2F;she should retry after a few minutes. On the other hand, if a message queue is adopted here and all the resources are assigned, then a user’s request will store in the queue and the user will need to wait for the response for unforeseeable time, which does not improve user experiences compare to the first case.</li></ul><h5 id="From-System-Performance-Perspective"><a href="#From-System-Performance-Perspective" class="headerlink" title="From System Performance Perspective"></a>From System Performance Perspective</h5><p>Well, it is obvious that introducing message queue will not improve system performance.</p><p>Therefore, for “Resources Competition”, adopting a message queue is an overengineering option.</p><h4 id="More-than-20-Requests-issue"><a href="#More-than-20-Requests-issue" class="headerlink" title="More than 20 Requests issue"></a>More than 20 Requests issue</h4><p>I was thinking about forcing a user to wait for 3 seconds as an alternative option, i.e. each time the user consumes the resource and takes less than 3 seconds (60 seconds &#x2F; 20 &#x3D; 3), then force the user to wait until 3 seconds passed (3 seconds count from taking the ownership of the resource to release it), it ensures that the user will not trigger the 20 requests per minutes error.</p><h5 id="From-User-Experiences-Perspective-1"><a href="#From-User-Experiences-Perspective-1" class="headerlink" title="From User Experiences Perspective"></a>From User Experiences Perspective</h5><p>Consider the fact that more then 98% of chance that a user’s request will take more than 3 seconds to complete, it means that forcing a user to wait for 3 seconds passed if a request takes less than 3 seconds will not significantly impact user experiences.</p><h5 id="From-System-Performance-Perspective-1"><a href="#From-System-Performance-Perspective-1" class="headerlink" title="From System Performance Perspective"></a>From System Performance Perspective</h5><p>One resource per message queue add heavy burden on the backend system while it does not improve the system performance in any means compare to the forcing wait method.</p><p>Therefore, for “More than 20 Requests issue”, using message queue solution is an overengineering option too.</p><p>With the arguments and conclusions in mind, the other day, I successfully convince the team to avoid adopting message queue which relief the backend dev team :).</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Overengineering is a word that a software engineer will hear about from time to time through his&amp;#x2F;her whole career. I recalled a desi</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Architecture" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/"/>
    
    <category term="System Design" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/System-Design/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Software Architechture" scheme="https://www.jingjies-blog.com/tags/Software-Architechture/"/>
    
    <category term="Message Queue" scheme="https://www.jingjies-blog.com/tags/Message-Queue/"/>
    
  </entry>
  
  <entry>
    <title>Integrate the Payment System Into a Small Business</title>
    <link href="https://www.jingjies-blog.com/2023/07/27/integrate-payment-system-into-small-business/"/>
    <id>https://www.jingjies-blog.com/2023/07/27/integrate-payment-system-into-small-business/</id>
    <published>2023-07-27T20:41:37.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, I helped some small business (less then 20 people) integrating payment module into their systems. I consider that it worths sharing the experiences, as this also benefit the cases include: selling code on Github, selling online courses on personal websites etc.</p><h2 id="The-payment-platform-manages-everything-for-you"><a href="#The-payment-platform-manages-everything-for-you" class="headerlink" title="The payment platform manages everything for you"></a>The payment platform manages everything for you</h2><p>There are some payment platforms that manage everything for you. You do not need to bother to design the frontend UIs of “placing order” page (choosing the products listed on the page) and “paying” page (filling credit card information and actually transferring the money), not to mention the backend logics behind the UIs. The platforms do all these things for you. Let us take <a href="https://www.lemonsqueezy.com/">lemon squeezy</a> as an example(Well, I swear I did not receive ads fee from <strong>lemon squeezy</strong>, but maybe they should do so :D).</p><p>For integrating <strong>lemon squeezy</strong> into a system, all a software engineer needs to do is:</p><ul><li><p>Set a store page on <strong>lemon squeezy</strong>, fill the page with information includes: product information and payment information, as shown below. The customers should complete all the steps regarding buying products on the store page: select products, filling payment information and transferring money.</p><figure><img src="https://i.imgur.com/N0tE27Y.png" alt="Trulli" style="width:100%" /><figcaption align = "center"><b>Fig.1 - An example of lemon squeezy store page</b></figcaption></figure></li><li><p>Add a link to the frontend page where you would like to redirect the customers to the store page of <strong>lemon squeezy</strong></p></li><li><p>Add a page for customers to activate the keys recieved from <strong>lemon squeezy</strong>. After a customer complete payment on the store page, <strong>lemon squeezy</strong> will return an activation key to the customer, the customer should use the key to claim the ownership of the product on the website. On the backend of the website, when activating the key, the software engineer needs to do two things: 1) validate the key with <strong>lemon squeezy</strong>; 2) activate the key with <strong>lemon squeezy</strong>.</p></li></ul><p>With such payment platform, the software engineer only need to save the activation key and the order id returned from <strong>lemon squeezy</strong>. <strong>lemon squeezy</strong> provide a orders page for the store owners to check order information, as shown below.</p><figure>  <img src="https://i.imgur.com/f4Hh1QV.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Fig.1 - The orders page provided by **lemon squeezy** </b>  </figcaption></figure><h3 id="Cons"><a href="#Cons" class="headerlink" title="Cons:"></a>Cons:</h3><p>There is no perfect solution in reality. The cons of using <strong>lemon squeezy</strong> as I observed from my own experiences are:</p><ul><li><p><strong>User experience</strong>. Rediect to a totally seperate payment page affect user experiences regarding online purchasing in a bad way. In some countries, the page loading speed may need up to 30 seconds.</p></li><li><p><strong>Trust issue</strong>. As the domain name for the <strong>lemon squeezy</strong> store page is different from the source site, it may create trust issue for the potential customers</p></li><li><p><strong>Less control</strong>. High managed platform means little spaces for customization. For instance, the layout of the store page UIs are not in the control.</p></li></ul><h2 id="Type-2-Manage-everything-on-your-own"><a href="#Type-2-Manage-everything-on-your-own" class="headerlink" title="Type 2: Manage everything on your own"></a>Type 2: Manage everything on your own</h2><p>For this type of platform, a typical work flow is:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select products --&gt; select payment method --&gt; place the order to the platform via RestAPI --&gt; the platform returns a qrcode link for the customer to pay --&gt; nortify the website the payment has completed</span><br></pre></td></tr></table></figure><p>in which you have everything in control, i.e. design UIs of products page and payment page, handling all the backend logics.</p><h3 id="Pros-and-Cons"><a href="#Pros-and-Cons" class="headerlink" title="Pros and Cons"></a>Pros and Cons</h3><p>The cons of adopting the type of platform is that it may takes time to build it as everything is under your control. And the pros are clearly the opposite side of the cons of type 1 platform.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Recently, I helped some small business (less then 20 people) integrating payment module into their systems. I consider that it worths sha</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Architecture" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/"/>
    
    <category term="System Design" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/System-Design/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Database" scheme="https://www.jingjies-blog.com/tags/Database/"/>
    
    <category term="MongoDB" scheme="https://www.jingjies-blog.com/tags/MongoDB/"/>
    
    <category term="Payment" scheme="https://www.jingjies-blog.com/tags/Payment/"/>
    
  </entry>
  
  <entry>
    <title>Use Async in React Scenarios</title>
    <link href="https://www.jingjies-blog.com/2023/05/05/react-scenarios-use-async/"/>
    <id>https://www.jingjies-blog.com/2023/05/05/react-scenarios-use-async/</id>
    <published>2023-05-05T20:43:51.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>In a typical software architect that seperates backend (<strong>Nodejs</strong>) and frontend (<strong>React</strong>), the frontend is quite ‘light weighted’ in terms of <strong>operations regarding data</strong> (Also grâce à the tools like react-redux, the frontend development is easier). That means we normally do not need to cope with scenarios that needs to write complex Nodejs code on the frontend side as it is just a viewer. However, due to certain features of React, there are some scenarios that we need to pay attention to. In this article, I summarise two typical scenarios that using ‘async’ within React in a recent project.</p><h2 id="Calling-Async-Method-in-‘useEffect’"><a href="#Calling-Async-Method-in-‘useEffect’" class="headerlink" title="Calling Async Method in ‘useEffect’"></a>Calling Async Method in ‘useEffect’</h2><p>There are some cases that we need to call async method in <em>useEffct</em>. Can we do things like below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">useEffect</span>(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">await</span> axios.<span class="title function_">post</span>(...)</span><br><span class="line">  ...</span><br><span class="line">&#125;, [])</span><br></pre></td></tr></table></figure><p>No, we cannot. Why? Because the async axios call returns promise and the useEffect does not expect callback function to return promise. The useEffect waits for the return of nothing or a function.</p><p>Then what we should do? There are two methods</p><ul><li>Define an async function inside <em>useEffect</em></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> axios.<span class="title function_">post</span>(...)</span><br><span class="line">  &#125;()</span><br><span class="line">  ...</span><br><span class="line">&#125;, [])</span><br></pre></td></tr></table></figure><ul><li>Define an async function outside <em>useEffect</em></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getData = useCallBack (<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">await</span> axios.<span class="title function_">post</span>(...)</span><br><span class="line">  ...</span><br><span class="line">&#125;, [])</span><br><span class="line"></span><br><span class="line"><span class="title function_">useEffect</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="title function_">getData</span>()</span><br><span class="line">&#125;, [getData])</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="Use-Async-in-‘setInterval’"><a href="#Use-Async-in-‘setInterval’" class="headerlink" title="Use Async in ‘setInterval’"></a>Use Async in ‘setInterval’</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setInterval</span>(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">axios</span>(...) </span><br><span class="line">&#125;, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure><p>Can we do things like above in <em>setInterval</em>? It is questionable, as the async call may cost more than the time set in <em>setInterval</em> method. So what is the better option? Use <em>Promise</em> with <em>setTimeout</em>, like below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params">ms</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> <span class="built_in">setTimeout</span>(resolve, ms))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">loop</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">while</span> (<span class="comment">/* condition */</span>) &#123;</span><br><span class="line">    <span class="comment">/* code to wait on goes here (sync or async) */</span>    </span><br><span class="line"></span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">delay</span>(<span class="number">100</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Reference: <a href="https://stackoverflow.com/questions/51830200/how-to-await-inside-setinterval-in-js">https://stackoverflow.com/questions/51830200/how-to-await-inside-setinterval-in-js</a></p><p>The above are the two scenarios that I met in my project, when I look back as a new full-stack engineer (I was heavily involved in backend dev, not frontend):)</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;In a typical software architect that seperates backend (&lt;strong&gt;Nodejs&lt;/strong&gt;) and frontend (&lt;strong&gt;React&lt;/strong&gt;), the frontend is q</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="React" scheme="https://www.jingjies-blog.com/categories/Technology/React/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="React" scheme="https://www.jingjies-blog.com/tags/React/"/>
    
  </entry>
  
  <entry>
    <title>Thinking in MongoDB TTL Indexes</title>
    <link href="https://www.jingjies-blog.com/2023/04/28/MongoDB-TTL-Indexes/"/>
    <id>https://www.jingjies-blog.com/2023/04/28/MongoDB-TTL-Indexes/</id>
    <published>2023-04-28T12:25:36.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Let us start with requirements</p><h2 id="Business-Requirements"><a href="#Business-Requirements" class="headerlink" title="Business Requirements"></a>Business Requirements</h2><p>The system needs to accept subcriptions from users with certain period time. After the paid subscription expires, the subscription status in user profile should be downgraded from a subscriped user to a normal user.</p><h2 id="Make-A-Choice-among-Two-Options"><a href="#Make-A-Choice-among-Two-Options" class="headerlink" title="Make A Choice among Two Options"></a>Make A Choice among Two Options</h2><h3 id="MongoDB-TTL-Index-vs-Node-Schedule"><a href="#MongoDB-TTL-Index-vs-Node-Schedule" class="headerlink" title="MongoDB TTL Index vs Node Schedule"></a>MongoDB TTL Index vs Node Schedule</h3><p>As introduced in one of my previous writing regarding the <a href="https://www.jingjies-blog.com/blog/2023/04/20/a-discussion-about-system-design-in-real-case/">System Architect</a> , the system uses MongoDB to store user data. To add the function into the system, there were two choices that immediately came to my mind: MongoDB TTL Indexes and Node Schedule</p><ul><li><p><strong>Node Schedule</strong><br>Node schedule runs on the server side, for example, <a href="https://www.npmjs.com/package/node-schedule">node-schedule</a>. But since the requirement is to change user’s subscription status after certain amount of time (relatively long, like a week, a month etc.), and it will only run once, putting such burden on the server side for such task is not worth it.</p></li><li><p><strong>MongoDB TTL Indexes</strong><br>Compare to Node Schedule, MongoDB TTL Indexes run on the database side, it will not create extra jobs on the server, it is a better choice for such requirement.</p></li></ul><h2 id="MongoDB-TTL-Indexes"><a href="#MongoDB-TTL-Indexes" class="headerlink" title="MongoDB TTL Indexes"></a>MongoDB TTL Indexes</h2><h3 id="Directly-create-MongoDB-TTL-Indexes"><a href="#Directly-create-MongoDB-TTL-Indexes" class="headerlink" title="Directly create MongoDB TTL Indexes"></a>Directly create MongoDB TTL Indexes</h3><p>Clearly, in my case, I need to create MongoDB TTL Indexes with give parameters after the schema is generated. And after searching for a while, I realised that in mongoose, I can only create TTL indexes when create the schema, it is not possible to create TTL indexes with give parameters when the app is running (please correct me if I am wrong here). Within mongoose, it only can introduce TTL Index within schema definition, as shown below:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> mongoose <span class="keyword">from</span> <span class="string">&#x27;mongoose&#x27;</span>;</span><br><span class="line"><span class="keyword">const</span> &#123; <span class="title class_">Schema</span> &#125; = mongoose;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">XXXSchema</span> = <span class="keyword">new</span> <span class="title class_">Schema</span>(</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">username</span>: &#123;</span><br><span class="line">      <span class="attr">type</span>: <span class="title class_">String</span>,</span><br><span class="line">      <span class="attr">required</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="attr">unique</span>: <span class="literal">true</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// timestamp of the creation data of the user</span></span><br><span class="line">  &#123; <span class="attr">timestamps</span>: <span class="literal">true</span> &#125;</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="title class_">XXXSchema</span>.<span class="title function_">index</span>(</span><br><span class="line">  &#123; <span class="attr">createdAt</span>: <span class="number">1</span> &#125;,</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">expireAfterSeconds</span>: <span class="number">30</span>,</span><br><span class="line">    <span class="attr">partialFilterExpression</span>: &#123; <span class="attr">username</span>: &#123; <span class="attr">$eq</span>: <span class="string">&#x27;xxx&#x27;</span> &#125; &#125;,</span><br><span class="line">  &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h3 id="Indirectly-create-MongoDB-TTL-Indexes"><a href="#Indirectly-create-MongoDB-TTL-Indexes" class="headerlink" title="Indirectly create MongoDB TTL Indexes"></a>Indirectly create MongoDB TTL Indexes</h3><p>Luckily, there is an alternative method to save my life: using <code>expireAt</code> in the schema. Under the table, the <code>expireAt</code> generates TTL indexes in MongoDB. Below are how I did it:</p><ol><li>introduce <code>expireAt</code> in the schema with <code>expires</code> property</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> mongoose <span class="keyword">from</span> <span class="string">&#x27;mongoose&#x27;</span></span><br><span class="line"><span class="keyword">const</span> &#123; <span class="title class_">Schema</span> &#125; = mongoose</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">XXXSchema</span> = <span class="keyword">new</span> <span class="title class_">Schema</span>(</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">username</span>: &#123;</span><br><span class="line">      <span class="attr">type</span>: <span class="title class_">String</span>,</span><br><span class="line">      <span class="attr">required</span>: <span class="literal">true</span>,</span><br><span class="line">      <span class="attr">unique</span>: <span class="literal">true</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">expireAt</span>: &#123;</span><br><span class="line">      <span class="attr">type</span>: <span class="title class_">Date</span>,</span><br><span class="line">      <span class="attr">default</span>: <span class="title class_">Date</span>.<span class="property">now</span>,</span><br><span class="line">      <span class="attr">expires</span>: <span class="number">0</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// timestamp of the creation data of the user</span></span><br><span class="line">  &#123; <span class="attr">timestamps</span>: <span class="literal">true</span> &#125;,</span><br><span class="line">)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><ol start="2"><li>when receiving the parameters, create the data with <code>expireAt</code> is set.</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">const</span> newXXX = <span class="keyword">new</span> <span class="title function_">XXX</span>(&#123;</span><br><span class="line">  <span class="attr">username</span>: user.<span class="property">username</span>,</span><br><span class="line">  <span class="attr">expireAt</span>: <span class="title class_">Date</span>.<span class="title function_">now</span>() + duration * <span class="number">1000</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> newXXX.<span class="title function_">save</span>();</span><br><span class="line">...</span><br></pre></td></tr></table></figure><ol start="3"><li>After the code in step 2 is ran, in the MongoDB, a TTL index should be seem. Let us take MongoDB Atlas as an example:<br><img src="https://i.imgur.com/qwVQjlY.png" alt="Image"></li></ol><p>Job done :)</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Let us start with requirements&lt;/p&gt;
&lt;h2 id=&quot;Business-Requirements&quot;&gt;&lt;a href=&quot;#Business-Requirements&quot; class=&quot;headerlink&quot; title=&quot;Business Req</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Architecture" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/"/>
    
    <category term="Backend" scheme="https://www.jingjies-blog.com/categories/Backend/"/>
    
    <category term="Backend" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/Backend/"/>
    
    <category term="Database" scheme="https://www.jingjies-blog.com/categories/Backend/Database/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Database" scheme="https://www.jingjies-blog.com/tags/Database/"/>
    
    <category term="Backend" scheme="https://www.jingjies-blog.com/tags/Backend/"/>
    
    <category term="MongoDB" scheme="https://www.jingjies-blog.com/tags/MongoDB/"/>
    
  </entry>
  
  <entry>
    <title>A Chit-chat about System Design in Real Case</title>
    <link href="https://www.jingjies-blog.com/2023/04/20/a-discussion-about-system-design-in-real-case/"/>
    <id>https://www.jingjies-blog.com/2023/04/20/a-discussion-about-system-design-in-real-case/</id>
    <published>2023-04-20T18:10:57.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, I have been busy working on a project start from sctrach – from business idea to a software product running online. I literally complete the design and most of coding job myself (well, I also deeply involded in devops as well). The project is about to go online now. I would like to spend a little bit of time to discuss about the system design of the project.</p><h2 id="Business-Idea"><a href="#Business-Idea" class="headerlink" title="Business Idea"></a>Business Idea</h2><p>As it is a chit-chat about system design in real case, I will briefly introduce the business without mentioning too many details.</p><p>The system provides a kind of service for registered users that is based on purchased usage quota, i.e., everytime a user consume the service, the number of service usage limit belongs to the user will be reduced by ONE, until it reaches to ZERO. Then the user needs to further purchase the service.</p><p>In essence, the system needs:</p><ul><li>a user management service</li><li>a mechanism that is able to change the user data in ‘real time’</li></ul><h2 id="System-Design"><a href="#System-Design" class="headerlink" title="System Design"></a>System Design</h2><figure>  <img src="https://i.imgur.com/dy5iCL4.jpg" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>System Design for the Business</b>  </figcaption></figure><h3 id="User-Management-Service"><a href="#User-Management-Service" class="headerlink" title="User Management Service"></a>User Management Service</h3><p>A typical user management module, I chose mongodb for the following 3 reasons:</p><ul><li><p>the user schema design was not fixed at the beginning, to avoid data schema changing troubles with relational DB, I went with NoSQL</p></li><li><p>The better sharding and scalability provied by mongodb as it is json document based.</p></li><li><p>To change the user data in ‘real-time’, I consider caching user data in memory for fast I&#x2F;O, and I only want to cache partial user data, not all of them, therefore, NoSQL is a better option.</p></li></ul><h3 id="Change-User-Data-in-‘Real-time’"><a href="#Change-User-Data-in-‘Real-time’" class="headerlink" title="Change User Data in ‘Real-time’"></a>Change User Data in ‘Real-time’</h3><p>As I mentioned, everytime a user consumes the service, the service usage quota will minus ONE. Since the service could be used by multiple users at the same time, the system must not spend too much time on I&#x2F;O regading changing the user data, hence, I consider use in-memory cache here in stead of updating data directly in mongodb. That is the place where Redis will play.</p><p>As shown in the figure, when a user login, the user data will be loaded into Redis, and everytime the user consumes the service, the backend side will update the data in he Redis, and only write back to mongoDB when the user logout.</p><h3 id="Sync-user-data-between-frontend-and-backend"><a href="#Sync-user-data-between-frontend-and-backend" class="headerlink" title="Sync user data between frontend and backend"></a>Sync user data between frontend and backend</h3><p>At the frontend side, the user may need to see the number of service usage in ‘real-time’. There are two choices:</p><ul><li>Frontend always keey the data sync with backend, means that everytime the service is used, the frontend will invoke REST API of backend and wait for returned result.</li><li>Frontend and backend use different data set. To be more specific, the frontend caches the user data in react-redux (only work with data from react-redux), and everytime the user consumes the service, on the frontend side, it change the number in react-redux, at the same time, invoke REST API to update the number in redis on the backend side.</li></ul><p>For the 1st choice, the frontend side will always show the correct data but it sacrifices time. For the 2nd one, the frontend may show different data from backend (if something wrong happens on the backend side regarding updating data in redis), but the frontend side does not need to wait for the REST API call result.</p><p>I went for the 2nd choice for speed.</p><h3 id="Locking-the-Serivce"><a href="#Locking-the-Serivce" class="headerlink" title="Locking the Serivce"></a>Locking the Serivce</h3><p>The service will be used by multiple users at the same time and it is not sharable. Therefore, I need a distributed lock here.</p><p>Since I have introduced redis for caching, I used redis redlock for distributed locking.</p><p>These are the design decisions I have made during the project. System design is always about trade-offs: space, time, cost. The most import one: do not over design, the priority is to meet the business requirements not to create a technically perfect product.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Recently, I have been busy working on a project start from sctrach – from business idea to a software product running online. I literally</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Architecture" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/"/>
    
    <category term="System Design" scheme="https://www.jingjies-blog.com/categories/Software-Architecture/System-Design/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="MongoDB" scheme="https://www.jingjies-blog.com/tags/MongoDB/"/>
    
    <category term="Software Architechture" scheme="https://www.jingjies-blog.com/tags/Software-Architechture/"/>
    
    <category term="Redis" scheme="https://www.jingjies-blog.com/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>Loading Testing and Benchmark A Set of REST APIs using AutoCannon</title>
    <link href="https://www.jingjies-blog.com/2022/09/15/load-testing-autocannon/"/>
    <id>https://www.jingjies-blog.com/2022/09/15/load-testing-autocannon/</id>
    <published>2022-09-15T17:44:59.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, I need to do load tests for a set of REST APIs under Node.js environment. After studying some online articles, I decided to give it a go with <a href="https://github.com/mcollina/autocannon#readme">AutoCannon</a>. My scenario is that I have already had a sets of requests setup in <a href="https://www.postman.com/">Postman</a>, and I do not want to rewrite everything for the load test. Luckily, I found a solution that exactly match my requirement (listed below), but I still want to write my own version from a AutoCannon fresher’s perspective and hopefully will be useful for future readers.</p><h3 id="Step-1-Export-A-Collection-of-Requests-from-Postman"><a href="#Step-1-Export-A-Collection-of-Requests-from-Postman" class="headerlink" title="Step.1 Export A Collection of Requests from Postman"></a>Step.1 Export A Collection of Requests from Postman</h3><p>As shown below, left click the <strong>“…”</strong> button at the right side of the requests collection. Then choose <strong>“Export”</strong> in the popup menu.</p><figure>  <img src="https://i.imgur.com/v7nucig.png" alt="Trulli" style="width:50%" />  <figcaption align = "center"><b>Fig.1 - Export Requests Collection</b>  </figcaption></figure> Afer this step, we should receive a JON file contains all the information of the REST APIs we would like to test.<h3 id="Step-2-Write-Code-for-Load-Testing"><a href="#Step-2-Write-Code-for-Load-Testing" class="headerlink" title="Step.2 Write Code for Load Testing"></a>Step.2 Write Code for Load Testing</h3><p>We need to create a sepearte <em>xxx.js</em> file that tells <strong>AutoCannon</strong> what to do.</p><ul><li>Load request data from exported JSON file</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> autocannon = <span class="built_in">require</span>(<span class="string">&#x27;autocannon&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs/promises&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// read array of items from exported .json file from postman</span></span><br><span class="line"><span class="keyword">let</span> entries = <span class="literal">undefined</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">getRequests</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> data = <span class="keyword">await</span> fs.<span class="title function_">readFile</span>(</span><br><span class="line">    <span class="string">&#x27;./youtube_clone_proj.postman_collection.json&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;UTF-8&#x27;</span></span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  entries = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(data).<span class="property">item</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>Set up AutoCannon for Each Request</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">entries.<span class="title function_">map</span>(<span class="keyword">async</span> (entry) =&gt; &#123;</span><br><span class="line">    <span class="comment">// there are multi request in item</span></span><br><span class="line">    entry.<span class="property">item</span>.<span class="title function_">filter</span>(<span class="function">(<span class="params">ele</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// filter the empty request</span></span><br><span class="line">      <span class="keyword">return</span> ele.<span class="property">request</span>.<span class="property">url</span> !== <span class="literal">undefined</span>;</span><br><span class="line">    &#125;).<span class="title function_">map</span>(<span class="keyword">async</span> (ele) =&gt; &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(ele.<span class="property">request</span>.<span class="property">method</span> + <span class="string">&quot; &quot;</span> + ele.<span class="property">request</span>.<span class="property">url</span>.<span class="property">raw</span>);</span><br><span class="line">      <span class="keyword">const</span> result = <span class="keyword">await</span> <span class="title function_">autocannon</span>(&#123;</span><br><span class="line">        <span class="attr">url</span>: ele.<span class="property">request</span>.<span class="property">url</span>.<span class="property">raw</span>,</span><br><span class="line">        <span class="attr">method</span>: ele.<span class="property">request</span>.<span class="property">method</span>,</span><br><span class="line">        <span class="attr">connections</span>: <span class="number">100</span>,</span><br><span class="line">        <span class="attr">workers</span>: <span class="number">50</span>,</span><br><span class="line">        <span class="attr">duration</span>: <span class="number">5</span>,</span><br><span class="line">        <span class="attr">body</span>: ele.<span class="property">request</span>.<span class="property">body</span> === <span class="literal">undefined</span>? <span class="literal">null</span> : <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(ele.<span class="property">request</span>.<span class="property">body</span>),</span><br><span class="line">         <span class="comment">// read other options here: https://github.com/mcollina/autocannon#autocannonopts-cb</span></span><br><span class="line">      &#125;, finishedBench);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// track process</span></span><br><span class="line">      autocannon.<span class="title function_">track</span>(result, &#123;<span class="attr">renderProgressBar</span>: <span class="literal">false</span>&#125;);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// this is used to kill the instance on CTRL-C</span></span><br><span class="line">      process.<span class="title function_">once</span>(<span class="string">&#x27;SIGINT&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">        result.<span class="title function_">stop</span>()</span><br><span class="line">      &#125;)</span><br><span class="line"></span><br><span class="line">      <span class="keyword">function</span> <span class="title function_">finishedBench</span> (err, res) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;finished bench&#x27;</span>, err, res)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure><h3 id="Launch-the-test"><a href="#Launch-the-test" class="headerlink" title="Launch the test"></a>Launch the test</h3><p>In the terminal window, run the following</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node xxx.js</span><br></pre></td></tr></table></figure><p>Then we should able to see output like this for each invidual request:</p><figure>  <img src="https://i.imgur.com/Qbef9U0.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Fig.2 - API1 result</b>  </figcaption></figure><figure>  <img src="https://i.imgur.com/IfroyrM.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Fig.3 - API2 result</b>  </figcaption></figure> For sure, there are more details left to discover, e.g. settings of _autocannon_, but that is left for reading and searching the official document :)<h3 id="reference"><a href="#reference" class="headerlink" title="reference"></a>reference</h3><p><a href="https://stackoverflow.com/questions/64927794/benchmark-express-apis-with-autocannon-from-postman-collection">Benchmark express apis with autocannon from postman collection</a></p><h4 id="You-are-more-then-welcome-to-site-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link"><a href="#You-are-more-then-welcome-to-site-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link" class="headerlink" title="You are more then welcome to site the content of this blog, but please do indicate the original reference link."></a>You are more then welcome to site the content of this blog, but please do indicate the original reference link.</h4>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Recently, I need to do load tests for a set of REST APIs under Node.js environment. After studying some online articles, I decided to giv</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Testing" scheme="https://www.jingjies-blog.com/categories/Software-Testing/"/>
    
    <category term="Load Test" scheme="https://www.jingjies-blog.com/categories/Software-Testing/Load-Test/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Load Test" scheme="https://www.jingjies-blog.com/tags/Load-Test/"/>
    
    <category term="Autocannon" scheme="https://www.jingjies-blog.com/tags/Autocannon/"/>
    
  </entry>
  
  <entry>
    <title>Using Winston Logger in Node.js</title>
    <link href="https://www.jingjies-blog.com/2022/09/04/using-winston-logger-in-nodejs/"/>
    <id>https://www.jingjies-blog.com/2022/09/04/using-winston-logger-in-nodejs/</id>
    <published>2022-09-04T17:08:01.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://www.npmjs.com/package/winston">Winston</a> logger is commonly used logger in Node.js. Most of the case, I use <a href="https://www.npmjs.com/package/winston">winston</a> logger and also integration outputs of <a href="https://www.npmjs.com/package/morgan">morgan</a> into <em>winston</em>. I recalled that the first time I used <em>winston</em>, I followed some of the online tutorials regarding how to config and use it, but there are always problems here and there for my scenarios. Therefore, I decide to write this to blog to introduce how I config <em>winston</em> logger in my projects.</p><h2 id="Project-Structure"><a href="#Project-Structure" class="headerlink" title="Project Structure"></a>Project Structure</h2><p>I normally put the configuration file of <em>winston</em> logger like the following:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Project</span><br><span class="line">| ...</span><br><span class="line">├───middleware</span><br><span class="line">├───models</span><br><span class="line">├───config</span><br><span class="line">│   ├───winston.js</span><br><span class="line">│ ...</span><br><span class="line">├───app.js</span><br><span class="line">├───package.json</span><br><span class="line">| ...</span><br></pre></td></tr></table></figure><p>Then in <em>*.js</em> files, it will use the <em>winston</em> config file like the following: </p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> winstonLogger = <span class="built_in">require</span>(<span class="string">&#x27;./config/winston&#x27;</span>);</span><br><span class="line">...</span><br><span class="line">winstonLogger.<span class="title function_">error</span>(<span class="string">`<span class="subst">$&#123;err.status || <span class="number">500</span>&#125;</span> - <span class="subst">$&#123;err.message&#125;</span> - <span class="subst">$&#123;req.originalUrl&#125;</span> - <span class="subst">$&#123;req.method&#125;</span> - <span class="subst">$&#123;req.ip&#125;</span>`</span>);</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h2 id="Winston-Configuration-File-Explained"><a href="#Winston-Configuration-File-Explained" class="headerlink" title="Winston Configuration File Explained"></a>Winston Configuration File Explained</h2><p>The conent of the configuration file <em>winston.js</em> mentioned in the previous section is shown below:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> appRoot = <span class="built_in">require</span>(<span class="string">&#x27;app-root-path&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> winston = <span class="built_in">require</span>(<span class="string">&#x27;winston&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> &#123; format, transports &#125; = winston;</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> colors = &#123;</span><br><span class="line">  <span class="attr">error</span>: <span class="string">&#x27;red&#x27;</span>,</span><br><span class="line">  <span class="attr">warn</span>: <span class="string">&#x27;yellow&#x27;</span>,</span><br><span class="line">  <span class="attr">info</span>: <span class="string">&#x27;green&#x27;</span>,</span><br><span class="line">  <span class="attr">http</span>: <span class="string">&#x27;magenta&#x27;</span>,</span><br><span class="line">  <span class="attr">debug</span>: <span class="string">&#x27;white&#x27;</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">winston.<span class="title function_">addColors</span>(colors);</span><br><span class="line"></span><br><span class="line"><span class="comment">// define the custom settings for each transport (file, console)</span></span><br><span class="line"><span class="keyword">const</span> logger = winston.<span class="title function_">createLogger</span>(&#123;</span><br><span class="line">  <span class="attr">level</span>: <span class="string">&#x27;http&#x27;</span>,</span><br><span class="line">  <span class="attr">format</span>: format.<span class="title function_">combine</span>(</span><br><span class="line">    format.<span class="title function_">timestamp</span>(&#123;</span><br><span class="line">      <span class="attr">format</span>: <span class="string">&#x27;YYY-MM-DD HH:mm:ss&#x27;</span>,</span><br><span class="line">    &#125;),</span><br><span class="line">    format.<span class="title function_">errors</span>(&#123; <span class="attr">stack</span>: <span class="literal">true</span> &#125;),</span><br><span class="line">    format.<span class="title function_">splat</span>(),</span><br><span class="line">    format.<span class="title function_">printf</span>(</span><br><span class="line">      <span class="function">(<span class="params">info</span>) =&gt;</span> <span class="string">`<span class="subst">$&#123;info.timestamp&#125;</span> <span class="subst">$&#123;info.level&#125;</span>: <span class="subst">$&#123;info.message&#125;</span>`</span>,</span><br><span class="line">    ),</span><br><span class="line">    format.<span class="title function_">json</span>(),</span><br><span class="line">  ),</span><br><span class="line">  <span class="attr">defaultMeta</span>: &#123; <span class="attr">service</span>: <span class="string">&#x27;quickpost&#x27;</span> &#125;,</span><br><span class="line">  <span class="attr">transports</span>: [</span><br><span class="line">    <span class="keyword">new</span> transports.<span class="title class_">File</span>(&#123; <span class="attr">filename</span>: path.<span class="title function_">join</span>(appRoot.<span class="title function_">toString</span>(), <span class="string">&#x27;/logs/error.log&#x27;</span>), <span class="attr">level</span>: <span class="string">&#x27;error&#x27;</span> &#125;),</span><br><span class="line">    <span class="keyword">new</span> transports.<span class="title class_">File</span>(&#123; <span class="attr">filename</span>: path.<span class="title function_">join</span>(appRoot.<span class="title function_">toString</span>(), <span class="string">&#x27;/logs/combined.log&#x27;</span>) &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> !== <span class="string">&#x27;production&#x27;</span>) &#123;</span><br><span class="line">  logger.<span class="title function_">add</span>(<span class="keyword">new</span> transports.<span class="title class_">Console</span>(&#123;</span><br><span class="line">    <span class="attr">format</span>: format.<span class="title function_">combine</span>(</span><br><span class="line">      <span class="comment">// print all the message colored</span></span><br><span class="line">      format.<span class="title function_">colorize</span>(&#123; <span class="attr">all</span>: <span class="literal">true</span> &#125;),</span><br><span class="line">      format.<span class="title function_">printf</span>(</span><br><span class="line">        <span class="function">(<span class="params">info</span>) =&gt;</span> <span class="string">`<span class="subst">$&#123;info.timestamp&#125;</span> <span class="subst">$&#123;info.level&#125;</span>: <span class="subst">$&#123;info.message&#125;</span>`</span>,</span><br><span class="line">      ),</span><br><span class="line">      format.<span class="title function_">simple</span>(),</span><br><span class="line">    ),</span><br><span class="line">  &#125;));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">logger.<span class="property">stream</span> = &#123;</span><br><span class="line">  <span class="attr">write</span>: <span class="function">(<span class="params">message</span>) =&gt;</span> logger.<span class="title function_">http</span>(message),</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = logger;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>To explain the above sourcecode in details:</p><h4 id="Set-Colors-for-Logger-Levels"><a href="#Set-Colors-for-Logger-Levels" class="headerlink" title="Set Colors for Logger Levels"></a>Set Colors for Logger Levels</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> colors = &#123;</span><br><span class="line">  <span class="attr">error</span>: <span class="string">&#x27;red&#x27;</span>,</span><br><span class="line">  <span class="attr">warn</span>: <span class="string">&#x27;yellow&#x27;</span>,</span><br><span class="line">  <span class="attr">info</span>: <span class="string">&#x27;green&#x27;</span>,</span><br><span class="line">  <span class="attr">http</span>: <span class="string">&#x27;magenta&#x27;</span>,</span><br><span class="line">  <span class="attr">debug</span>: <span class="string">&#x27;white&#x27;</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">winston.<span class="title function_">addColors</span>(colors);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="Config-Other-Settings-of-Winston"><a href="#Config-Other-Settings-of-Winston" class="headerlink" title="Config Other Settings of Winston"></a>Config Other Settings of Winston</h4><p>For the following settings, please refer to the official site of <em>winston</em> logger. I would emphasize two things:</p><ul><li><p>On the <strong><em>transports</em></strong> part. In the following configuration, I put loggers into files under the directory <em>&#x2F;log&#x2F;*.log</em> files. That is a very basic settings, you can of course adding settings like, generate new log files after the current log file reach to certain size, and further config organizing log files according to their generated date.</p></li><li><p>If not in “production” environment, I set the logger still output on the console for facilitating debugging.</p></li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// define the custom settings for each transport (file, console)</span></span><br><span class="line"><span class="keyword">const</span> logger = winston.<span class="title function_">createLogger</span>(&#123;</span><br><span class="line">  <span class="attr">level</span>: <span class="string">&#x27;http&#x27;</span>,</span><br><span class="line">  <span class="attr">format</span>: format.<span class="title function_">combine</span>(</span><br><span class="line">    format.<span class="title function_">timestamp</span>(&#123;</span><br><span class="line">      <span class="attr">format</span>: <span class="string">&#x27;YYY-MM-DD HH:mm:ss&#x27;</span>,</span><br><span class="line">    &#125;),</span><br><span class="line">    format.<span class="title function_">errors</span>(&#123; <span class="attr">stack</span>: <span class="literal">true</span> &#125;),</span><br><span class="line">    format.<span class="title function_">splat</span>(),</span><br><span class="line">    format.<span class="title function_">printf</span>(</span><br><span class="line">      <span class="function">(<span class="params">info</span>) =&gt;</span> <span class="string">`<span class="subst">$&#123;info.timestamp&#125;</span> <span class="subst">$&#123;info.level&#125;</span>: <span class="subst">$&#123;info.message&#125;</span>`</span>,</span><br><span class="line">    ),</span><br><span class="line">    format.<span class="title function_">json</span>(),</span><br><span class="line">  ),</span><br><span class="line">  <span class="attr">defaultMeta</span>: &#123; <span class="attr">service</span>: <span class="string">&#x27;quickpost&#x27;</span> &#125;,</span><br><span class="line">  <span class="attr">transports</span>: [</span><br><span class="line">    <span class="keyword">new</span> transports.<span class="title class_">File</span>(&#123; <span class="attr">filename</span>: path.<span class="title function_">join</span>(appRoot.<span class="title function_">toString</span>(), <span class="string">&#x27;/logs/error.log&#x27;</span>), <span class="attr">level</span>: <span class="string">&#x27;error&#x27;</span> &#125;),</span><br><span class="line">    <span class="keyword">new</span> transports.<span class="title class_">File</span>(&#123; <span class="attr">filename</span>: path.<span class="title function_">join</span>(appRoot.<span class="title function_">toString</span>(), <span class="string">&#x27;/logs/combined.log&#x27;</span>) &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> !== <span class="string">&#x27;production&#x27;</span>) &#123;</span><br><span class="line">  logger.<span class="title function_">add</span>(<span class="keyword">new</span> transports.<span class="title class_">Console</span>(&#123;</span><br><span class="line">    <span class="attr">format</span>: format.<span class="title function_">combine</span>(</span><br><span class="line">      <span class="comment">// print all the message colored</span></span><br><span class="line">      format.<span class="title function_">colorize</span>(&#123; <span class="attr">all</span>: <span class="literal">true</span> &#125;),</span><br><span class="line">      format.<span class="title function_">printf</span>(</span><br><span class="line">        <span class="function">(<span class="params">info</span>) =&gt;</span> <span class="string">`<span class="subst">$&#123;info.timestamp&#125;</span> <span class="subst">$&#123;info.level&#125;</span>: <span class="subst">$&#123;info.message&#125;</span>`</span>,</span><br><span class="line">      ),</span><br><span class="line">      format.<span class="title function_">simple</span>(),</span><br><span class="line">    ),</span><br><span class="line">  &#125;));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Merge-Morgan-into-Winston"><a href="#Merge-Morgan-into-Winston" class="headerlink" title="Merge Morgan into Winston"></a>Merge Morgan into Winston</h4><p>The following code will merge morgan into <em>winston</em>.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">logger.<span class="property">stream</span> = &#123;</span><br><span class="line">  <span class="attr">write</span>: <span class="function">(<span class="params">message</span>) =&gt;</span> logger.<span class="title function_">http</span>(message),</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="Config-Morgan"><a href="#Config-Morgan" class="headerlink" title="Config Morgan"></a>Config Morgan</h5><p>To make <em>winston</em> working with <em>morgan</em>, we will need to add the following morgan settings: </p><ul><li>File directory: <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Project</span><br><span class="line">| ...</span><br><span class="line">├───middleware</span><br><span class="line">│   ├───morgan.js</span><br><span class="line">│ ...</span><br><span class="line">├───app.js</span><br><span class="line">├───package.json</span><br><span class="line">| ...</span><br></pre></td></tr></table></figure></li><li>Config morgan:<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> morgan = <span class="built_in">require</span>(<span class="string">&#x27;morgan&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> logger = <span class="built_in">require</span>(<span class="string">&#x27;../config/winston&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> morganMiddleware = <span class="title function_">morgan</span>(</span><br><span class="line">  <span class="comment">// Define message format string (this is the default one).</span></span><br><span class="line">  <span class="comment">// The message format is made from tokens, and each token is</span></span><br><span class="line">  <span class="comment">// defined inside the Morgan library.</span></span><br><span class="line">  <span class="comment">// You can create your custom token to show what do you want from a request.</span></span><br><span class="line">  <span class="string">&#x27;combined&#x27;</span>,</span><br><span class="line">  <span class="comment">// Options: in this case, I overwrote the stream and the skip logic.</span></span><br><span class="line">  <span class="comment">// See the methods above.</span></span><br><span class="line">  &#123; <span class="attr">stream</span>: logger.<span class="property">stream</span> &#125;,</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = morganMiddleware;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>Use in app.js<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">const</span> morganMiddleware = <span class="built_in">require</span>(<span class="string">&#x27;./middleware/morgan&#x27;</span>);</span><br><span class="line">...</span><br><span class="line">app.<span class="title function_">use</span>(morganMiddleware);</span><br><span class="line">...</span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ul><h4 id="If-things-go-well-…"><a href="#If-things-go-well-…" class="headerlink" title="If things go well …"></a>If things go well …</h4><p>You should be able to see logger in the console like the following:<br><img src="https://i.imgur.com/1Y8WVy5.png" alt="Image"></p><p>And also the same records can be find in the log files: <em>&#x2F;log&#x2F;*.log</em> files.</p><h5 id="You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link"><a href="#You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link" class="headerlink" title="You are more then welcome to cite the content of this blog, but please do indicate the original reference link."></a>You are more then welcome to cite the content of this blog, but please do indicate the original reference link.</h5>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/winston&quot;&gt;Winston&lt;/a&gt; logger is commonly used logger in Node.js. Most of the case, I use &lt;a href=&quot;h</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Javascript" scheme="https://www.jingjies-blog.com/tags/Javascript/"/>
    
    <category term="Winston Logger" scheme="https://www.jingjies-blog.com/tags/Winston-Logger/"/>
    
  </entry>
  
  <entry>
    <title>Hitting Real Database? Unit Test and Integration Test for RESTful API in Nodejs Environment</title>
    <link href="https://www.jingjies-blog.com/2022/08/03/ut-it-restful-api-in-nodejs/"/>
    <id>https://www.jingjies-blog.com/2022/08/03/ut-it-restful-api-in-nodejs/</id>
    <published>2022-08-03T21:57:23.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h2><p>While talking about TDD within developing web systems, we cannot avoid testing RESTful APIs. I have seen some turtorials online (e.g. the articles which discuss about how to do unit test for REST API via leveraging certain npm packages under Nodejs environment) which mention that for Unit Test (hereafter, UT) it will hit database.</p><p>Well, in my humble option, in UT we should not hit database. The core idea of unit test is “<strong>Isolation</strong>“, i.e. isolate the function&#x2F;methods under testing, presume everthing that the function&#x2F;methods will interact with works as expected, and see if the function&#x2F;methods generate the result we are hoping for. Therefore, if hitting database in UT, the testing result of the UT will depend on the status of the database, the coupling between the function&#x2F;methods and the database makes the test is not a real “Unit Test”. Tests that requires access database should categories as Integration Test (hereafter IT) or End-to-End test. In the rest of this article, I will explain in details regarding how to do UT and IT for RESTful APIs with sourcecode samples provided under Nodejs environment.</p><h3 id="Integration-Test-for-RESTful-API-under-Nodejs-Environment-using-MongoDB-and-supertest-as-an-example"><a href="#Integration-Test-for-RESTful-API-under-Nodejs-Environment-using-MongoDB-and-supertest-as-an-example" class="headerlink" title="Integration Test for RESTful API under Nodejs Environment: using MongoDB and supertest as an example"></a>Integration Test for RESTful API under Nodejs Environment: using MongoDB and supertest as an example</h3><p>Assume that we want to test the following REST API: “<em>POST &#x2F;api&#x2F;auth&#x2F;signup</em>“, we have the following code in our project: </p><p><strong>app.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> authRouter <span class="keyword">from</span> <span class="string">&#x27;./routes/auth-route.js&#x27;</span>;</span><br><span class="line">...</span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&#x27;/api/auth&#x27;</span>, authRouter);</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><strong>auth-route.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">import</span> &#123;</span><br><span class="line">  signup,</span><br><span class="line">  signin,</span><br><span class="line">&#125; <span class="keyword">from</span> <span class="string">&#x27;../controllers/auth-controller.js&#x27;</span>;</span><br><span class="line">...</span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/signup&#x27;</span>, signup);</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>To test the api in integration test, we will need to complete two steps:</p><ol><li>connect to database for testing purpose only, not database in production environment</li><li>test whether the api output expected results</li></ol><h4 id="1-Set-a-Mongodb-connection-selector-between-production-environment-and-testing-environment"><a href="#1-Set-a-Mongodb-connection-selector-between-production-environment-and-testing-environment" class="headerlink" title="1. Set a Mongodb connection selector between production environment and testing environment"></a>1. Set a Mongodb connection selector between production environment and testing environment</h4><p><strong>app.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// According to the value of process.env, choose the connection between prod env and test env</span></span><br><span class="line"><span class="keyword">const</span> mongodbConnect = process.<span class="property">env</span>.<span class="property">NODE_ENV</span> === <span class="string">&quot;test&quot;</span>? </span><br><span class="line">  process.<span class="property">env</span>.<span class="property">MONGODB_CLOUD_TEST</span> : process.<span class="property">env</span>.<span class="property">MONGODB_CLOUD_PROD</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">dbConnect</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">await</span> mongoose.<span class="title function_">connect</span>(mongodbConnect);</span><br><span class="line">    winstonLogger.<span class="title function_">info</span>(<span class="string">`Connect to mongodb of <span class="subst">$&#123;process.env.NODE_ENV&#125;</span>`</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (err) &#123;</span><br><span class="line">    winstonLogger.<span class="title function_">error</span>(<span class="string">`Failed to connect to mongodb due to <span class="subst">$&#123;err&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="2-Using-supertest-to-complete-the-test"><a href="#2-Using-supertest-to-complete-the-test" class="headerlink" title="2. Using supertest to complete the test"></a>2. Using <a href="https://www.npmjs.com/package/supertest">supertest</a> to complete the test</h4><p><strong>auth-route.test.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">describe</span>(<span class="string">&#x27;POST signup&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> tmpUser = &#123;</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&#x27;testaa&#x27;</span>,</span><br><span class="line">    <span class="attr">email</span>: <span class="string">&#x27;testaa@gmail.com&#x27;</span>,</span><br><span class="line">    <span class="attr">password</span>: <span class="string">&#x27;1234&#x27;</span></span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">it</span>(<span class="string">&#x27;post a new user and respond with 200 and a msg showes that user has been created&#x27;</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="comment">// a real call to the database in testing environment</span></span><br><span class="line">    <span class="keyword">const</span> &#123; text, status &#125; = <span class="keyword">await</span> <span class="title function_">request</span>(app)</span><br><span class="line">      .<span class="title function_">post</span>(<span class="string">&#x27;/api/auth/signup&#x27;</span>)</span><br><span class="line">      .<span class="title function_">set</span>(<span class="string">&#x27;Accept&#x27;</span>, <span class="string">&#x27;application/json&#x27;</span>)</span><br><span class="line">      .<span class="title function_">send</span>(tmpUser);</span><br><span class="line"></span><br><span class="line">    <span class="title function_">expect</span>(status).<span class="property">to</span>.<span class="property">be</span>.<span class="title function_">equal</span>(<span class="number">200</span>);</span><br><span class="line">    <span class="title function_">expect</span>(text).<span class="property">to</span>.<span class="property">be</span>.<span class="title function_">equal</span>(<span class="string">&#x27;User has been created!&#x27;</span>)</span><br><span class="line">  &#125;);</span><br><span class="line">  ...</span><br><span class="line">&#125;);</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>As it can be seen from the above code snippet, the supertest makes a call to the database for creating a user profile, and the user profile is actually created in the database under test environment. Since the test involving testing the connection with a real database, I would like to categories this type of tests as integration test.</p><p>Then, how the unit test for the REST API should look like?</p><h3 id="Unit-Test-for-RESTful-API-under-Nodejs-Environment-using-MongoDB-and-supertest-nock-as-an-example"><a href="#Unit-Test-for-RESTful-API-under-Nodejs-Environment-using-MongoDB-and-supertest-nock-as-an-example" class="headerlink" title="Unit Test for RESTful API under Nodejs Environment: using MongoDB and supertest + nock as an example"></a>Unit Test for RESTful API under Nodejs Environment: using MongoDB and supertest + nock as an example</h3><p>To clearly tell the difference between Integration Test and Unit Test, in this part I will still use supertest as the agent (alternative option can be <a href="https://github.com/axios/axios">axios</a>, for example) to file a request to the “<em>POST &#x2F;api&#x2F;auth&#x2F;signup</em>“, but in Unit Test, I will not let the request hitting datacase via http server, instead, I use <a href="https://github.com/nock/nock">nock</a> to intercept the request and return the expected result, as shown blow:</p><pre><code class="js">describe(&#39;POST signup&#39;, () =&gt; &#123;  const tmpUser = &#123;    name: &#39;testaa&#39;,    email: &#39;testaa@gmail.com&#39;,    password: &#39;1234&#39;  &#125;;  it(&#39;post a new user and respond with 200 and a msg showes that user has been created&#39;, async () =&gt; &#123;    // file http request to the REST API    const signupUser = async () =&gt; &#123;      return await request(&#39;http://test.com&#39;)        .post(&#39;/api/auth/signup&#39;)        .set(&#39;Accept&#39;, &#39;application/json&#39;)        .send(tmpUser);    &#125;;    // mock http server: check if the server received expected params in request.body, then reply expected response     nock(&#39;http://test.com&#39;)      .post(&#39;/api/auth/signup&#39;, body =&gt; &#123;        expect(body.name).to.be.equal(&#39;testaa&#39;);        expect(body.email).to.be.equal(&#39;testaa@gmail.com&#39;);        expect(body.password).to.be.equal(&#39;1234&#39;);        return true;      &#125;)      .reply(200, &#39;User has been created!&#39;);    const &#123; text, status &#125; = await signupUser();    expect(status).to.be.equal(200);    expect(text).to.be.equal(&#39;User has been created!&#39;)  &#125;);  ...&#125;);</code></pre><p>In the above unit test, I use <strong>nock</strong> to mock a http server. The <em>supertest</em> still file a request, but the request will be intercepted by the mock server and return expected results, instead of reaching a real server and further hitting a real database. This is the way how isolation is achieved: I am not coupling the test with a real database, based on the assumption that all other parts works okay (via mocking), everything is completed within the test method.</p><p>A further questions may be asked is that what is point to do a Unit Test like this? Should I include the unit tests in my project? The answer is <strong>Yes</strong> and <strong>No</strong>.</p><ul><li><p>No: if you are developing a relatively small system as software vendor for a small business, or you are structuring a new software product that are targeting a small number of users, not aiming for millions or even billions users in the future, then I would say do not bother to add such unit test in your system. The ROI is not worth it, keep the integration test that check the connection with real database is enough.</p></li><li><p>Yes: if you are developing a system either big or small in a big company, or if you have a software product that is aiming for millions of daily users in the future (which means the product will be owned by a big company), I would say you probably will need to add the unit tests in your system. The reason? I would quote a saying from a movie called &lt;<a href="https://www.imdb.com/title/tt0320661/">Kingdom of Heaven</a>&gt;: nothing, everything.</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Background&quot;&gt;&lt;a href=&quot;#Background&quot; class=&quot;headerlink&quot; title=&quot;Background&quot;&gt;&lt;/a&gt;Background&lt;/h2&gt;&lt;p&gt;While talking about TDD within develop</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Software Testing" scheme="https://www.jingjies-blog.com/categories/Software-Testing/"/>
    
    <category term="Unit Test" scheme="https://www.jingjies-blog.com/categories/Software-Testing/Unit-Test/"/>
    
    <category term="Integration Test" scheme="https://www.jingjies-blog.com/categories/Software-Testing/Integration-Test/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Unit Test" scheme="https://www.jingjies-blog.com/tags/Unit-Test/"/>
    
    <category term="Integration Test" scheme="https://www.jingjies-blog.com/tags/Integration-Test/"/>
    
    <category term="Restful API" scheme="https://www.jingjies-blog.com/tags/Restful-API/"/>
    
    <category term="Nockjs" scheme="https://www.jingjies-blog.com/tags/Nockjs/"/>
    
    <category term="supertest" scheme="https://www.jingjies-blog.com/tags/supertest/"/>
    
  </entry>
  
  <entry>
    <title>Academia Way vs Industrial Way of Getting Things Done</title>
    <link href="https://www.jingjies-blog.com/2022/07/01/Academia-vs-Industry/"/>
    <id>https://www.jingjies-blog.com/2022/07/01/Academia-vs-Industry/</id>
    <published>2022-07-01T23:01:38.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Behind-the-Blog"><a href="#Behind-the-Blog" class="headerlink" title="Behind the Blog"></a>Behind the Blog</h2><p>I have experiences of both working in academia (at PhD level) and industry (for some of the big names). The transform between the two was a lesson for me and I would like to share it with the ones who are in the situation of the following:</p><ul><li>Do not know if should choose academia or industry after undergraduate study or receiving Master’s degree</li><li>Thinking of leaving adademia (after completing a PhD - Congratulations! That is a huge achievement already :), or quiting a PhD) and go to the industry, but do not know if it is the right choice.</li></ul><h3 id="Academia-Way-of-Getting-Things-Done"><a href="#Academia-Way-of-Getting-Things-Done" class="headerlink" title="Academia Way of Getting Things Done"></a>Academia Way of Getting Things Done</h3><p>Academia way of getting things done is to learn first (learn things related to your research area), then try to find the breakthrough. That means, you will need some time to do the preparations before actually begin to do something.</p><p>All the secrets behind getting success in acedemia, either as a PhD candicate or a PhD degree owner who are in the way of getting a tenure, to sum up in one sentence is: finding a<b>“niche”</b> (i.e. the so called breakthrough).</p><p>So how can I find the <b>“niche”</b> ? You may ask. In my experience, that is two steps work:</p><ol><li><p>First, you will need to build solid foudation in the researcing area. Basically, you will need to have a solid understanding of the common theories in the area you are working on. The stage can be completed by reading a grate a mount of publishes, books… It takes time and is a must step. Imaging you are presenting in a conference, in the Q&amp;A session, someone critisize what you just said by quoting a famous theory in the area, you do not know much about the theory and then you do not know “how to fight back”… </p></li><li><p>Second, after harnesss yourself with the knowledge you need after stage 1, then you can begin to do your actual work: try to find something new or “breakthrough” in the area that can work on.</p></li></ol><p>In short, in academia, you will be allowed time to ramp up before doing something.</p><h3 id="Industrial-Way-of-Getting-Things-Done"><a href="#Industrial-Way-of-Getting-Things-Done" class="headerlink" title="Industrial Way of Getting Things Done"></a>Industrial Way of Getting Things Done</h3><p>The industrial way of achieving something is not like the academia way. There is very limited time for praparation, it is more like learning by practicing. </p><p>While working in the industry, you will have to move much faster, build something tangible that can be evaluate by stakeholders, collect feedbacks, then improve it, then re-evalueate … Therefore, there is no time to do decent preparation, you learn things in the process of solving problems. The key in the industry is to have something that can prove the feasibility of an idea as soon as possible and push to the market.</p><h3 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h3><p>Thinking about how the <a href="https://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional-ebook/dp/B000SEIBB8">GoF Design Patterns</a> was discovered. If it was in Academia, the way of find the patterns would be: tons of readings, then propose some hypothesis, verify it via experiments and finally publish the findings (i.e. design patterns). However, in reality it was not like that, the GoF reviews the previous practices in software industry, then found those patterns. Exactly, the patterns were invented by software engineers in practice without a deliberate purpose (they were found by “variable”. some of most impactful things was invented by accidents, check <a href="https://www.amazon.com/Antifragile-Things-That-Disorder-Incerto/dp/0812979680">Anti Fragile</a>. </p><p>There are not right or wrong in these two ways of resolveing issues. Because they are in different contexts. In industry, most of the time, we are dealing with practical problem and need to see impacts right away; but in academia, we are dealing with the issues for the future in many case.    </p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Behind-the-Blog&quot;&gt;&lt;a href=&quot;#Behind-the-Blog&quot; class=&quot;headerlink&quot; title=&quot;Behind the Blog&quot;&gt;&lt;/a&gt;Behind the Blog&lt;/h2&gt;&lt;p&gt;I have experiences</summary>
      
    
    
    
    <category term="Miscellaneous" scheme="https://www.jingjies-blog.com/categories/Miscellaneous/"/>
    
    <category term="Life" scheme="https://www.jingjies-blog.com/categories/Miscellaneous/Life/"/>
    
    
    <category term="Academia" scheme="https://www.jingjies-blog.com/tags/Academia/"/>
    
    <category term="PhD" scheme="https://www.jingjies-blog.com/tags/PhD/"/>
    
    <category term="Industry" scheme="https://www.jingjies-blog.com/tags/Industry/"/>
    
  </entry>
  
  <entry>
    <title>Let&#39;s Encrypt with Subdomain(www) on Heroku</title>
    <link href="https://www.jingjies-blog.com/2022/06/23/lets-encrypt-with-subdomain-on-heroku/"/>
    <id>https://www.jingjies-blog.com/2022/06/23/lets-encrypt-with-subdomain-on-heroku/</id>
    <published>2022-06-23T16:46:13.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h3><p>I spent a few weeks to develop and set up my own website. The registered domain is root domain (i.e. <em>example.com</em>) and the DNS setting on GoDaddy is to redirect the root domain(<em>example.com</em>) to the <em>www</em> subdomain (e.g. <em><a href="http://www.example.com/">www.example.com</a></em>). The last step before publishing is to add SSL certificates to my root domain and www subdomain. I found a nice tutorial online regarding how to do it <a href="https://groovecoder.com/2017/01/03/letsencrypt-heroku-dns">Let’s Encrypt on Heroku with DNS Domain Validation</a>. However, it turned out that follow the intructions I could not generate certificate and private key for my www subdomain. I will show the issue and explain how I resolve it in details.</p><figure>  <img src="https://i.imgur.com/AYDb2XI.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Lovely "Lock":)</b>  </figcaption></figure> <h3 id="The-Issue"><a href="#The-Issue" class="headerlink" title="The Issue"></a>The Issue</h3><p>My ultimate goal is to let <em><a href="https://www.example.com/">https://www.example.com</a></em> work on browsers. I have three custom domain in my Heroku app: <em>*.exmaple.com</em>, <em>exmaple.com</em>, <em><a href="http://www.example.com/">www.example.com</a></em>.</p><p>Following the instructions of <a href="https://groovecoder.com/2017/01/03/letsencrypt-heroku-dns">Let’s Encrypt on Heroku with DNS Domain Validation</a>, when <b>Let’s Encrypt</b> asked me to provide the domain name (as shown below), no matter I use <em>example.com</em> or <em><a href="http://www.example.com/">www.example.com</a></em></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Please enter the domain name(s) you would like on your certificate (comma and/or</span><br><span class="line">space separated) (Enter &#x27;c&#x27; to cancel):</span><br></pre></td></tr></table></figure><p>later at the stage of applying the certificate to Heroku “sudo heroku certs:add –type&#x3D;…&#x2F;certificate  …&#x2F;privatekey”, <mark>the heroku will not provide domain name <em><a href="http://www.example.com/">www.example.com</a></em> as an option for the certificate but only <em>example.com</em>. This leads to the result of <em><a href="http://www.example.com/">www.example.com</a></em> is not secured.</mark></p><h3 id="Resolution"><a href="#Resolution" class="headerlink" title="Resolution"></a>Resolution</h3><p>The resolution is easy but took me a lot of time to find it. The trick is using <b>wildcard subdomain</b>. At the stage of providing domain name to Let’s Encrypt, I used <em>*.exmample.com</em>, then later when I apply the generated certificate and private key to Heroku, it offers <em><a href="http://www.exmample.com/">www.exmample.com</a></em> as an option.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">=== Almost <span class="keyword">done</span>! Which of these domains on this application would you like this certificate associated with?</span><br><span class="line">? Select domains www.jingjies-blog.com</span><br></pre></td></tr></table></figure><p>Then check on Heroku, I can see the following:</p><figure>  <img src="https://i.imgur.com/X0C0fzx.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>The wildcard certificate</b>  </figcaption></figure> <figure>  <img src="https://i.imgur.com/3Efevfo.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Secured www.jingjies-blog.com</b>  </figcaption></figure> <p>On GoDaddy, change the redirect from <em><a href="http://www.example.com/">http://www.example.com</a></em> to <em><a href="https://www.example.com/">https://www.example.com</a></em></p><figure>  <img src="https://i.imgur.com/w5o3sQv.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Set up Forwarding</b>  </figcaption></figure> <p>Finally, I can see the “lock” on browser :)</p><h5 id="You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link"><a href="#You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link" class="headerlink" title="You are more then welcome to cite the content of this blog, but please do indicate the original reference link."></a>You are more then welcome to cite the content of this blog, but please do indicate the original reference link.</h5>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;Background&quot;&gt;&lt;a href=&quot;#Background&quot; class=&quot;headerlink&quot; title=&quot;Background&quot;&gt;&lt;/a&gt;Background&lt;/h3&gt;&lt;p&gt;I spent a few weeks to develop and set</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Series" scheme="https://www.jingjies-blog.com/categories/Series/"/>
    
    <category term="Building Blog" scheme="https://www.jingjies-blog.com/categories/Series/Building-Blog/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Hexo" scheme="https://www.jingjies-blog.com/tags/Hexo/"/>
    
    <category term="Express" scheme="https://www.jingjies-blog.com/tags/Express/"/>
    
    <category term="SSL" scheme="https://www.jingjies-blog.com/tags/SSL/"/>
    
  </entry>
  
  <entry>
    <title>Add View Counter to Hexo in Express</title>
    <link href="https://www.jingjies-blog.com/2022/06/22/add-view-counter-to-Hexo-in-Express/"/>
    <id>https://www.jingjies-blog.com/2022/06/22/add-view-counter-to-Hexo-in-Express/</id>
    <published>2022-06-22T19:18:17.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>I planned to add a site visit counter and page view counter into my blog (Hexo + Express.js). And by searching online, it seems that there is very limited information can be found on how to add such things on a Hexo Blog where:</p><ul><li>The Hexo blog is integrated into an existing Express server, i.e. the blog should be visited at <em>express_site&#x2F;blog</em></li><li>The theme I used for the blog does not have such feature added, means I cannot just add something to the _config.yml of the theme and the view counter will work as a charm. I will need to customize it.</li></ul><p>There was several options that I had in mind:</p><ul><li><b>Only customize Hexo blog and the theme</b> –&gt; I realize it is not feasible. I do not know how to let Hexo or the theme load customised .js file under Hexo + Express structure;</li><li><b>Load js from Express side</b> –&gt; I finally use this method. Let Express loading related js file as Express static files, the js file will add the data to the view counter in Hexo pages. Blow is what I got in the end.<figure><img src="https://i.imgur.com/7R2wovy.png" alt="Trulli" style="width:100%" /><figcaption align = "center"><b>View counter in blog post</b></figcaption></figure></li></ul><p>Now I will introduce how I achive it in details.</p><h3 id="Set-up-view-counter"><a href="#Set-up-view-counter" class="headerlink" title="Set up view counter"></a>Set up view counter</h3><ul><li><p>Set up <a href="https://firebase.google.com/">Firebase</a> (There are tons of tutorial out there, so I am not going to repeat it:))</p><p>There is only one thing I would like to emphasize is the database set up. The database should be Realtime Database, and the “rules” is set as below. I will explain in details later why this will not cause security issues.</p></li></ul><figure>  <img src="https://i.imgur.com/7AdJk3w.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Firebase Realtime Databse Rules</b>  </figcaption></figure> <ul><li>After register and setting up the Firebase, add the following code to the <em>hexo_blog_directory&#x2F;themes&#x2F;{your theme}&#x2F;layout&#x2F;xxx.ejs</em> where you want to show the view counter.</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">id</span>=<span class="string">&quot;site-visits&quot;</span>&gt;</span>Sitevisits: <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;count&quot;</span>&gt;</span>--<span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">span</span> <span class="attr">id</span>=<span class="string">&quot;page-views&quot;</span>&gt;</span>Pageviews: <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;count&quot;</span>&gt;</span>--<span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span>  </span><br></pre></td></tr></table></figure><ul><li><p>Add the following .js code to <em>express_proj&#x2F;public&#x2F;javascripts&#x2F;</em>:</p><p><b>Note</b>: </p><ul><li>I used Firebase SDK 9</li><li>The details of database read&#x2F;write APIs could be found at <a href="https://firebase.google.com/docs/reference/js/database.md#database_package">Firebase Database APIs</a>   </li><li>You could of course test data read &#x2F; write sepeartely before using the whole block</li></ul></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; initializeApp &#125; <span class="keyword">from</span> <span class="string">&#x27;https://www.gstatic.com/firebasejs/9.8.3/firebase-app.js&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123;</span><br><span class="line">  getDatabase, ref, get, set, child,</span><br><span class="line"><span class="comment">// eslint-disable-next-line import/no-unresolved</span></span><br><span class="line">&#125; <span class="keyword">from</span> <span class="string">&#x27;https://www.gstatic.com/firebasejs/9.8.3/firebase-database.js&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">viewCounter</span> = <span class="keyword">async</span> (<span class="params"></span>) =&gt; &#123;</span><br><span class="line">  <span class="comment">// The web app&#x27;s Firebase configuration</span></span><br><span class="line">  <span class="comment">// For Firebase JS SDK v7.20.0 and later, measurementId is optional</span></span><br><span class="line">  <span class="keyword">const</span> firebaseConfig = &#123;</span><br><span class="line">    <span class="attr">apiKey</span>: <span class="string">&#x27;your api key&#x27;</span>,</span><br><span class="line">    <span class="attr">authDomain</span>: <span class="string">&#x27;your settings &#x27;</span>,</span><br><span class="line">    <span class="attr">databaseURL</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">    <span class="attr">projectId</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">    <span class="attr">storageBucket</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">    <span class="attr">messagingSenderId</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">    <span class="attr">appId</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">    <span class="attr">measurementId</span>: <span class="string">&#x27;xxx&#x27;</span>,</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Initialize Firebase</span></span><br><span class="line">  <span class="keyword">const</span> firebase = <span class="title function_">initializeApp</span>(firebaseConfig);</span><br><span class="line">  <span class="keyword">const</span> db = <span class="title function_">getDatabase</span>(firebase, firebaseConfig.<span class="property">databaseURL</span>);</span><br><span class="line">  <span class="keyword">const</span> oriUrl = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">host</span>;</span><br><span class="line">  <span class="keyword">const</span> curUrl = oriUrl + <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">pathname</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> <span class="title function_">readVisits</span> = <span class="keyword">async</span> (<span class="params">url, selector</span>) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> dbKey = <span class="built_in">decodeURI</span>(url.<span class="title function_">replace</span>(<span class="regexp">/\/|\./g</span>, <span class="string">&#x27;_&#x27;</span>));</span><br><span class="line">    <span class="keyword">let</span> count = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="title function_">get</span>(<span class="title function_">child</span>(<span class="title function_">ref</span>(db), dbKey));</span><br><span class="line">    <span class="keyword">if</span> (res.<span class="title function_">exists</span>()) &#123;</span><br><span class="line">      count = <span class="built_in">parseInt</span>(res.<span class="title function_">val</span>() || <span class="number">0</span>, <span class="number">10</span>) + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">await</span> <span class="keyword">await</span> <span class="title function_">set</span>(<span class="title function_">ref</span>(db, dbKey), count);</span><br><span class="line">    <span class="keyword">if</span> (selector.<span class="property">length</span> &gt; <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="comment">// eslint-disable-next-line no-param-reassign</span></span><br><span class="line">      selector[<span class="number">0</span>].<span class="property">innerText</span> = count;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">readVisits</span>(oriUrl, <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&#x27;.post-meta #site-visits .count&#x27;</span>));</span><br><span class="line">  <span class="keyword">if</span> (curUrl &amp;&amp; curUrl !== <span class="string">&#x27;_&#x27;</span>) &#123;</span><br><span class="line">    <span class="title function_">readVisits</span>(<span class="string">`page/<span class="subst">$&#123;curUrl&#125;</span>`</span>, <span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&#x27;.post-meta #page-views .count&#x27;</span>));</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title function_">viewCounter</span>();</span><br></pre></td></tr></table></figure><ul><li>After the above steps, the view counter is ready to go :)</li></ul><h3 id="Extra-issue-is-it-okay-to-expose-Firebase-apiKey"><a href="#Extra-issue-is-it-okay-to-expose-Firebase-apiKey" class="headerlink" title="Extra issue: is it okay to expose Firebase apiKey?"></a>Extra issue: is it okay to expose Firebase apiKey?</h3><p>When you see that I put <em>apiKey</em> etc. in a static .js file, you must have a question in mind: is that a security issue here? I had the same question. Then I found the following article: <a href="https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public">Is it safe to expose Firebase apiKey to the public?</a></p><p>The answer is a bit long, so I will sum up here: for a small size and not a critical site, it is okay to do thing like this. The <em>apiKey</em> here in Firebase is not like the traditional api keys we know, it is okay to expose it. All you need to do is to set up whitelist site for Firebase (<b>that is why I mentioend earlier in the article that you could set the Firebase database rules for opening read&#x2F;write public</b>). Here is how you can do it in details:</p><ul><li>Go to Firebase Console</li><li>Check Authentication Menu -&gt; Sign-in method tab</li><li>Scroll down to Authorized domains</li><li>Click “Add Domain” button and add your domains (localhost is added as default, and I do not delete as it is needed for local testing)</li></ul><figure>  <img src="https://i.imgur.com/HGbMNyR.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Set up whitelist site</b>  </figcaption></figure> <p>The above should be okay for a small site. I am not going to over-engineering it, but you can always tailor the security rules accoriding to your business requirements :) </p><h5 id="You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link"><a href="#You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link" class="headerlink" title="You are more then welcome to cite the content of this blog, but please do indicate the original reference link."></a>You are more then welcome to cite the content of this blog, but please do indicate the original reference link.</h5>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;I planned to add a site visit counter and page view counter into my blog (Hexo + Express.js). And by searching online, it seems that ther</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Series" scheme="https://www.jingjies-blog.com/categories/Series/"/>
    
    <category term="Building Blog" scheme="https://www.jingjies-blog.com/categories/Series/Building-Blog/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Hexo" scheme="https://www.jingjies-blog.com/tags/Hexo/"/>
    
    <category term="Express" scheme="https://www.jingjies-blog.com/tags/Express/"/>
    
    <category term="Firebase" scheme="https://www.jingjies-blog.com/tags/Firebase/"/>
    
  </entry>
  
  <entry>
    <title>Integerate Hexo with Existing Express Server</title>
    <link href="https://www.jingjies-blog.com/2022/06/18/hexo-integrate-express/"/>
    <id>https://www.jingjies-blog.com/2022/06/18/hexo-integrate-express/</id>
    <published>2022-06-18T20:00:02.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>I have a Express server and recently I would like to integrate <a href="https://hexo.io/">Hexo</a> blog framework into the existing Express.js site, for example, make the blog accessable on <em>express_site&#x2F;blog</em>. While exploring the feasibility, I realise that it is hardly to find a thorough tutorial of how to achieve it. There are some information can be found on:<br><a href="https://github.com/hexojs/hexo/issues/3644">Hexo Github Issue 3644</a> and <a href="https://github.com/smrsan76/x-hexo-app-connect#readme">x-heco-app-connect middleware</a>. However, none of them offer a complete guide of seamlessly integrating Hexo blog into an Express server. For example, following the instruction provided by <a href="https://github.com/smrsan76/x-hexo-app-connect#readme">x-heco-app-connect middleware</a>, the app will generate issues regarding routing. Therefore, I decide to write this blog to offer a tutorial of integrating Hexo into Express, i.e. make a Hexo blog as a route of existing Express server. </p><figure>  <img src="https://i.imgur.com/TyDFFrj.png" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Hexo under localhost:3000/blog</b>  </figcaption></figure> <p>My way of achieving it is based on <a href="https://github.com/smrsan76/x-hexo-app-connect#readme">x-hexo-app-connect</a> and some of the steps describe blow are based on the instruction provided by x-hexo-app-connect.</p><h2 id="Getting-Started"><a href="#Getting-Started" class="headerlink" title="Getting Started"></a>Getting Started</h2><p>1.1 Make a sub-directory under the express project directory. e.g. we make it under <em>experss_proj&#x2F;blog</em>, use this command under the project directory:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">mkdir</span> blog</span><br></pre></td></tr></table></figure><p>1.2 Enter the command to install <b>hexo-cli</b> globally</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo npm i hexo-cli -g</span><br></pre></td></tr></table></figure><p>1.3 Go to the blog’s directory (created in step 1.1) and enter the following command in terminal to init Hexo:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo init</span><br></pre></td></tr></table></figure><p>1.4 In the blog directory, enter the command in terminal to install <a href="https://github.com/smrsan76/x-hexo-app-connect#readme">x-hexo-app-connect</a> package:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm i x-hexo-app-connect --save</span><br></pre></td></tr></table></figure><p>1.5 Create a index.js file in the blog’s root directory. e.g. if you want to make it using a bash terminal (Mac platform), enter this command below:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">touch</span> index.js</span><br></pre></td></tr></table></figure><p>1.6 Fill in the index.js with the following code:</p><ul><li>for the return part, it is to config the Hexo blog, the <b>route</b> is the most important, that is where you will configure how the Hexo blog should be visited from the original Express server. In my case, the blog will be visited via route <em>express_site&#x2F;blog</em><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">Hexo</span> = <span class="built_in">require</span>(<span class="string">&#x27;hexo&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="function">(<span class="params">app</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> blogDirPath = __dirname;</span><br><span class="line">  <span class="keyword">const</span> hexo = <span class="keyword">new</span> <span class="title class_">Hexo</span>(blogDirPath, &#123;&#125;);</span><br><span class="line">  <span class="comment">// eslint-disable-next-line global-require</span></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">require</span>(<span class="string">&#x27;x-hexo-app-connect&#x27;</span>)(app, hexo, &#123; </span><br><span class="line">    <span class="comment">// The Configs/Options</span></span><br><span class="line">    <span class="attr">log</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">compress</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">header</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="attr">serveStatic</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="attr">route</span>: <span class="string">&quot;/blog&quot;</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>1.7 In the app.js (the home js file of Express), add the following to use the <a href="https://github.com/smrsan76/x-hexo-app-connect#readme">x-hexo-app-connect</a> in Express<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"><span class="comment">// Put it after app is initialized, as the app will be used as parameter</span></span><br><span class="line"><span class="keyword">const</span> blogRouter = <span class="built_in">require</span>(<span class="string">&#x27;./blog/index&#x27;</span>)(app);</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line">app.<span class="title function_">use</span>(blogRouter);</span><br><span class="line"></span><br></pre></td></tr></table></figure>1.8 This step is very important, it is to set “root” parameter in the _<em>config.yml</em> file under the root directory of Hexo. And set the <em>root</em> as <em>&#x2F;blog</em>. If not set this <em>root</em>, the Hexo blog home page can be visited via <em>express_site&#x2F;blog</em>, <mark>but when click the link of articles, categories, archives etc. the express will report 404 as the routes cannot be found</mark><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># <span class="variable constant_">URL</span></span><br><span class="line">## <span class="title class_">Set</span> your site url here. <span class="title class_">For</span> example, <span class="keyword">if</span> you use <span class="title class_">GitHub</span> <span class="title class_">Page</span>, set url <span class="keyword">as</span> <span class="string">&#x27;https://username.github.io/project&#x27;</span></span><br><span class="line"><span class="attr">url</span>: <span class="attr">http</span>:<span class="comment">//localhost:3000/</span></span><br><span class="line"><span class="attr">root</span>: /blog # <span class="keyword">for</span> locating themes <span class="keyword">static</span> files (.<span class="property">css</span> etc)</span><br></pre></td></tr></table></figure></li><li>If do not set <em>root</em> as <em>&#x2F;blog</em>, the following url will be generated as <em>localhost:3000&#x2F;all-categories</em> which will lead to 404 error.<figure><img src="https://i.imgur.com/9NVvAQ0.png" alt="Trulli" style="width:100%" /><figcaption align = "center"><b>./blog/all-categories</b></figcaption></figure></li></ul><p>1.9 Apply themes. You can just following the set up instruction of each specific theme, then it should be okay.<br>1.10 After all the above steps, run the Express server, if the terminal showes the following, then it means you are good:)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">INFO  Validating config</span><br><span class="line">INFO  Start processing</span><br><span class="line">INFO  [Hexo-App-Connect] is running on route /blog</span><br></pre></td></tr></table></figure><p>Hope this blog helps you in some way :)</p><h5 id="You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link"><a href="#You-are-more-then-welcome-to-cite-the-content-of-this-blog-but-please-do-indicate-the-original-reference-link" class="headerlink" title="You are more then welcome to cite the content of this blog, but please do indicate the original reference link."></a>You are more then welcome to cite the content of this blog, but please do indicate the original reference link.</h5>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;I have a Express server and recently I would like to integrate &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt; blog framework into the existing Expre</summary>
      
    
    
    
    <category term="Technology" scheme="https://www.jingjies-blog.com/categories/Technology/"/>
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/categories/Technology/Nodejs/"/>
    
    <category term="Series" scheme="https://www.jingjies-blog.com/categories/Series/"/>
    
    <category term="Building Blog" scheme="https://www.jingjies-blog.com/categories/Series/Building-Blog/"/>
    
    
    <category term="Nodejs" scheme="https://www.jingjies-blog.com/tags/Nodejs/"/>
    
    <category term="Hexo" scheme="https://www.jingjies-blog.com/tags/Hexo/"/>
    
    <category term="Express" scheme="https://www.jingjies-blog.com/tags/Express/"/>
    
    <category term="Javascript" scheme="https://www.jingjies-blog.com/tags/Javascript/"/>
    
  </entry>
  
  <entry>
    <title>My first blog</title>
    <link href="https://www.jingjies-blog.com/2022/06/16/first_blog/"/>
    <id>https://www.jingjies-blog.com/2022/06/16/first_blog/</id>
    <published>2022-06-16T22:40:12.000Z</published>
    <updated>2025-01-02T10:34:37.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="First-Blog"><a href="#First-Blog" class="headerlink" title="First Blog"></a>First Blog</h1><p>It is Jingjie here, it is my first blog on my own blogging site.I have always been wanting to have a place where I can write and post my own blogs. instead of writing things here and there. </p><h2 id="The-reason-of-writing-blog"><a href="#The-reason-of-writing-blog" class="headerlink" title="The reason of writing blog"></a>The reason of writing blog</h2><p>I am a passionate software engineer and by nature of this area, I learned lots of thing online (<b>i.e. I received help from others</b>) and wrote down a great number of notes (mainly on my own OneNote) regarding the knowledge I acquired: for strengthening my understanding of thing learned and picking them up in the future.</p><p>And for all these days working on academia and industry, I have my own understanding of software, technology etc. I would like to share them with others, and hoping they will be kind of helpful in some way (<b>i.e. I would like to return the favor</b>). </p><p>For this blog site, I will post my understanding of computer and life as well. Things start :)</p><figure>  <img src="https://i.imgur.com/eQt1qvm.jpg" alt="Trulli" style="width:100%" />  <figcaption align = "center"><b>Suprise of 2022:)</b>  </figcaption></figure> ]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;First-Blog&quot;&gt;&lt;a href=&quot;#First-Blog&quot; class=&quot;headerlink&quot; title=&quot;First Blog&quot;&gt;&lt;/a&gt;First Blog&lt;/h1&gt;&lt;p&gt;It is Jingjie here, it is my first blo</summary>
      
    
    
    
    <category term="Miscellaneous" scheme="https://www.jingjies-blog.com/categories/Miscellaneous/"/>
    
    <category term="Life" scheme="https://www.jingjies-blog.com/categories/Miscellaneous/Life/"/>
    
    
    <category term="Life" scheme="https://www.jingjies-blog.com/tags/Life/"/>
    
  </entry>
  
</feed>
