<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yogi Saputro</title>
    <description>The latest articles on DEV Community by Yogi Saputro (@yogski).</description>
    <link>https://dev.to/yogski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F494532%2F8e8dc7b0-91bf-407c-b42f-2d14dd6fe4ed.jpeg</url>
      <title>DEV Community: Yogi Saputro</title>
      <link>https://dev.to/yogski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yogski"/>
    <language>en</language>
    <item>
      <title>int8 Becomes String in PostgreSQL Node.JS</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Fri, 19 Jan 2024 07:39:45 +0000</pubDate>
      <link>https://dev.to/yogski/int8-becomes-string-in-postgresql-nodejs-3j8g</link>
      <guid>https://dev.to/yogski/int8-becomes-string-in-postgresql-nodejs-3j8g</guid>
      <description>&lt;p&gt;It is a brief explanation of why &lt;code&gt;int8&lt;/code&gt; data type in PostgreSQL is converted to &lt;code&gt;string&lt;/code&gt; instead of &lt;code&gt;number&lt;/code&gt; in Node.JS. tl;dr it is a feature, not bug.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t2v9kfyjc7e3gem82uh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t2v9kfyjc7e3gem82uh.png" alt="Dealing with large number is not easy" width="768" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But First, Little Story
&lt;/h2&gt;

&lt;p&gt;It can happen to anyone. Business requirement stated a transaction may be worth many billions in certain currencies. So, I needed to update SQL column data type from &lt;code&gt;int4&lt;/code&gt; to &lt;code&gt;int8&lt;/code&gt;. That was it? I supposed so. Turned out not. People were furious and they had thrown me into dark pit of debugging.&lt;/p&gt;

&lt;p&gt;I used Typescript but somehow it slipped through. I realized when taking a look on log and I saw long string like &lt;code&gt;8233800739007328003173000&lt;/code&gt;. It's enough clue that number addition became string concatenation. The returned query turned out as string. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But how?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was baffled for few moments, then regained composure. The chaos was fixed by casting those &lt;code&gt;string&lt;/code&gt; back to &lt;code&gt;number&lt;/code&gt;. After that, business flows as usual, people cheered, and I reclaimed my state of sanity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It's There, and Why It's Not A Bug
&lt;/h2&gt;

&lt;p&gt;The question lingers, though? Why did it turned into string? I did a little research and found out that it is expected behaviour. Yes, PostgreSQL &lt;code&gt;int8&lt;/code&gt; will always be converted to Node.JS &lt;code&gt;string&lt;/code&gt;. The culprit here is package called &lt;strong&gt;node-postgres&lt;/strong&gt; or &lt;code&gt;pg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you work with PostgreSQL in Node.JS, chances are there's &lt;code&gt;pg&lt;/code&gt; in your &lt;code&gt;node_modules&lt;/code&gt;. That's one of the packages that hold Node.JS community in one piece. Yet, exactly in this package the decision above was made. It is designed to handle one specific problem: Javascript &lt;code&gt;number&lt;/code&gt;. Now, let me explain a bit on &lt;code&gt;number&lt;/code&gt; before coming back.&lt;/p&gt;

&lt;p&gt;Javascript &lt;code&gt;number&lt;/code&gt; data type is 64-bit. Meanwhile, PostgreSQL &lt;code&gt;int8&lt;/code&gt; is also 64-bit. However, they are different. The first one is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding" rel="noopener noreferrer"&gt;IEEE 754 double precision&lt;/a&gt;, while the later is signed integer. Javascript &lt;code&gt;number&lt;/code&gt; has bits dedicated for precision and sacrifices range. Now let's compare.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;max value of Javascript &lt;code&gt;number&lt;/code&gt; (or &lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt;) is &lt;code&gt;2^53 - 1&lt;/code&gt; or 9.007.199.254.740.991. &lt;/li&gt;
&lt;li&gt;max value of PostgreSQL &lt;code&gt;int8&lt;/code&gt; is &lt;code&gt;2^63 - 1&lt;/code&gt; or 9.223.372.036.854.775.807&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, what will happen when the stored value in PostgreSQL is bigger than Javascript &lt;code&gt;number&lt;/code&gt; max value? Javascript will read the binary representation and churn out incorrect value.&lt;/p&gt;

&lt;p&gt;That is problem. BIG problem.&lt;br&gt;
That's where &lt;code&gt;pg&lt;/code&gt; package comes in and saves the day. The author recognized this problem, then decided to cast the result as Javascript &lt;code&gt;string&lt;/code&gt;, which does not alter the output value. &lt;/p&gt;

&lt;p&gt;For me it is beautiful trade-off. First, it handles all cases. Second, it avoids worst output. Third, it gives simple resolve once the value is safe in Javascript.&lt;/p&gt;

&lt;p&gt;So, hats-off for &lt;a href="https://github.com/brianc" rel="noopener noreferrer"&gt;Brian C&lt;/a&gt;, the author of &lt;code&gt;pg&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rerefences
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/brianc/node-postgres/issues/811" rel="noopener noreferrer"&gt;https://github.com/brianc/node-postgres/issues/811&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>postgres</category>
      <category>npm</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>One Product A Month for #DEVresolutions2024</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Fri, 12 Jan 2024 02:25:09 +0000</pubDate>
      <link>https://dev.to/yogski/one-product-a-month-for-devresolutions2024-263i</link>
      <guid>https://dev.to/yogski/one-product-a-month-for-devresolutions2024-263i</guid>
      <description>&lt;p&gt;The new year vibe has already settled in and people are getting more drawn into reality. That's when I exactly think this is the best time to make new year resolution. By starting on my own timeline, I know I write this not because of FOMO, but I aim for real progress, instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  My 2024 Resolution
&lt;/h2&gt;

&lt;p&gt;I managed to simple and ambitious enough goal in one sentence: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ship one product per end of month&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpwsxdfyitpr02nwmuxv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpwsxdfyitpr02nwmuxv.jpeg" alt="A shiny ambitious goal? _(source: playground.ai)_" width="512" height="512"&gt;&lt;/a&gt;&lt;br&gt;
This goal beautifully encompasses all things I aspire to accomplish.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open opportunities from various ways&lt;/li&gt;
&lt;li&gt;Focusing on small set of tasks instead of overthinking technical stuff that lead to self-inflicted scope creep (that's my bane, yeah)&lt;/li&gt;
&lt;li&gt;Experience journey beyond coding&lt;/li&gt;
&lt;li&gt;More portfolio is nice bonus as well&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Details, Details, and Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scope
&lt;/h3&gt;

&lt;p&gt;Then, isn't it daunting and overly ambitious? Yes, when we think about "product" as a set of solutions working seamlessly and displayed beautifully. I define output product below to set expectations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the product solves one specific real world problem&lt;/li&gt;
&lt;li&gt;the product is deployed on internet&lt;/li&gt;
&lt;li&gt;the product interacts with human users, and optionally with machine users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might be close to MVP, but not yet. The goal is personal growth, not user feedback or financial. Even in this setup, I will have total freedom to build with Svelte in January and Solid.js in February, and so on. I do not focus on product valuation. However, it is totally possible that output product from this process is further developed as MVP or, with with sufficient resources and time, marketable product.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does This Help DEV Users?
&lt;/h3&gt;

&lt;p&gt;In addition to shipping a prodcut per month, I will also share my journey on DEV about my problem-finding process, my design process, my journey of overcoming obstacles. Hopefully it will help community members.&lt;/p&gt;

&lt;p&gt;Wish me luck, and I hope everyone has a blessed and eventful 2024 🎊🎊🍾 🚀&lt;/p&gt;

</description>
      <category>devresolutions2024</category>
    </item>
    <item>
      <title>"Breaking Tables, Delivering Features: A Developer's Guide to SQL Normalization"</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Tue, 14 Nov 2023 08:58:41 +0000</pubDate>
      <link>https://dev.to/yogski/breaking-tables-delivering-features-a-developers-guide-to-sql-normalization-56p3</link>
      <guid>https://dev.to/yogski/breaking-tables-delivering-features-a-developers-guide-to-sql-normalization-56p3</guid>
      <description>&lt;p&gt;Your new feature will break sophisticated, well-thought, beautiful, current SQL database structure? Worry not. This guide is here to help fellow developers to handle feature that involves changing database structure. With example, of course.&lt;/p&gt;

&lt;p&gt;Please note that this guide is focused on normalizing SQL database. This is not related to programming language, nor this applies to NoSQL database.&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Change is Inevitable
&lt;/h2&gt;

&lt;p&gt;First, a little background. As time passes, product usually grows following business needs. Complexity also grows. You will have more feature tickets awaiting. On one hand, this is good sign. People are using the product, which turns into revenue, which turns into your salary. Hopefully.&lt;/p&gt;

&lt;p&gt;On the other hand, you will be pressed to work on new features, and more often than not, require changes on your codebase and database. It is not due to lack of planning. No one could cover all possible cases and put it into code. Therefore, the &lt;strong&gt;first tip is to embrace the change&lt;/strong&gt; and deal with it.&lt;/p&gt;

&lt;p&gt;I have encountered quite some breaking changes in my career. The early ones felt frustrating. I thought "They are messing with my desgin? Why didn't they thought about it?". Then sometimes I even doubt myself "Why didn't I thought about it?". That was bad. Then I realized it is just inherent reality of evolving product. After some more time, I finally developed simple guide to deal with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real World Example: NEW Split Bill Payment
&lt;/h2&gt;

&lt;p&gt;Suppose that your product is a restaurant management system that allows customer to see menu, make orders, and pay through app/website while dining-in. It reduces restaurant's staff cost and improve efficiency. &lt;/p&gt;

&lt;p&gt;Now the product have implemented order with payment. However, everyone agreed that one order is paid once. In other words, order and payment have &lt;code&gt;1:1&lt;/code&gt; relation. Few weeks later, the company received feedback. Now people want to split bills. This means &lt;code&gt;1:1&lt;/code&gt; relation between order and payment turns to &lt;code&gt;1:N&lt;/code&gt; relation.&lt;/p&gt;

&lt;p&gt;What do you have to do now? Keep reading and follow these steps&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Normalization Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Identify Impacted Tables and Columns
&lt;/h3&gt;

&lt;p&gt;Once it is known that some table relations will be changed from &lt;code&gt;1:1&lt;/code&gt; to &lt;code&gt;1:N&lt;/code&gt;, quickly determine which columns are impacted.&lt;/p&gt;

&lt;p&gt;In example case above, let's say that existing &lt;code&gt;orders&lt;/code&gt; table is structured like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;bill_base_amount&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;tax_amount&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;other_fee_amount&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;bill_total_amount&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_amount&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;payment_timestamp&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_completed&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the table &lt;code&gt;orders&lt;/code&gt; with sample data&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;order_date&lt;/th&gt;
&lt;th&gt;bill_base_amount&lt;/th&gt;
&lt;th&gt;tax_amount&lt;/th&gt;
&lt;th&gt;other_fee_amount&lt;/th&gt;
&lt;th&gt;bill_total_amount&lt;/th&gt;
&lt;th&gt;payment_amount&lt;/th&gt;
&lt;th&gt;payment_type&lt;/th&gt;
&lt;th&gt;payment_timestamp&lt;/th&gt;
&lt;th&gt;payment_completed&lt;/th&gt;
&lt;th&gt;created_at&lt;/th&gt;
&lt;th&gt;updated_at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2023-11-14 12:00:00&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;6200&lt;/td&gt;
&lt;td&gt;6200&lt;/td&gt;
&lt;td&gt;Credit Card&lt;/td&gt;
&lt;td&gt;2023-11-14 12:05:00&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;2023-11-14 12:00:00&lt;/td&gt;
&lt;td&gt;2023-11-14 12:05:00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this case, following columns should be separated as new table&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;payment_amount&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payment_type&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payment_timestamp&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;&lt;code&gt;payment_completed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why &lt;code&gt;bill_total_amount&lt;/code&gt; and some others are not included in new table? Because the information is related to order as a whole. Furthermore, for &lt;code&gt;bill_total_amount&lt;/code&gt; it is practical later as checker to make sure sum of all split bills are equal to bill on order.&lt;/p&gt;

&lt;p&gt;Once impacted columns are identified, you need to make new table.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create New Table
&lt;/h3&gt;

&lt;p&gt;Now, a new table is needed to store payment data. Such table requires&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifier --&amp;gt; &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Foreign key to &lt;code&gt;orders&lt;/code&gt; --&amp;gt; &lt;code&gt;order_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Impacted columns above&lt;/li&gt;
&lt;li&gt;Timestamps --&amp;gt; just good practice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In SQL, this is one example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;payment_amount&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;payment_timestamp&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_completed&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, you have the building blocks in the database. Time to start refactoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Change Existing Codebase Logic
&lt;/h3&gt;

&lt;p&gt;At this point, you'd know &lt;code&gt;orders&lt;/code&gt; and &lt;code&gt;payments&lt;/code&gt; has 1:N relation, meaning one order could have at least one payment. You just need to insert data to &lt;code&gt;orders&lt;/code&gt; and &lt;code&gt;payments&lt;/code&gt;. However, it's still unclear &lt;strong&gt;&lt;em&gt;when&lt;/em&gt;&lt;/strong&gt; to insert the data. There are at least 2 options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assuming there's &lt;code&gt;order_menu&lt;/code&gt; table (it's common sense for dev, right?), you can split by menu price. The splitting process is another part entirely, but from database perspective, all payment records can be generated and inserted together.&lt;/li&gt;
&lt;li&gt;Split by amount. This approach relies on amount input. This allow each payment to be generated independently. However, the total amount must be thoroughly checked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which one is better? It depends on what product team wants :)&lt;/p&gt;

&lt;p&gt;This could be an implementation in Node.js&lt;br&gt;
 Example in Node.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pgp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg-promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_connection_string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Sample data for an order&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;bill_base_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tax_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;other_fee_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bill_total_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payment_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payment_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Credit Card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Query for inserting into orders&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertOrderQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`INSERT INTO orders -- etc`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertPaymentQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`INSERT INTO payments -- etc`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mappingToPayments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do something mapping here&lt;/span&gt;
    &lt;span class="c1"&gt;// should be more complex, but you get the idea&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;paymentList&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Function to insert payments for a specific order_id&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insertPayments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertPaymentPromises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mappingToPayments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;paymentData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Modify paymentData to include the order_id for the query&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modifiedPaymentData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;paymentData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order_id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute the query to insert into payments&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;none&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertPaymentQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modifiedPaymentData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Return a promise that resolves when all payments are inserted&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertPaymentPromises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Execute the first query to insert into orders&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertOrderQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use the returned order_id in the function to insert payments&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Call the function to insert payments for the order_id&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;insertPayments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order and payments inserted successfully.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error inserting order and payments:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Close the database connection when done&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Also, don't forget to refactor the query to fetch data. Plus, make sure to test your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Migrate Existing Data
&lt;/h3&gt;

&lt;p&gt;Now that you've taken care of the development. It is time to migrate the data. As you've known, existing data is in &lt;code&gt;orders&lt;/code&gt;. After deployment, they better be in &lt;code&gt;payments&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;Fortunately, there's one-time query for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment_completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;bill_total_amount&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;payment_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- Assuming one payment for one order&lt;/span&gt;
  &lt;span class="n"&gt;payment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payment_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;payment_completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Assuming all existing payments are completed&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;payment_type&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- Add additional conditions if needed&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If there's more complexities (e.g not all payments are completed), you need to handle them first before executing query above.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Deploy &amp;amp; Run
&lt;/h3&gt;

&lt;p&gt;This is time where you actually execute all steps above in production environment. Fingers crossed!&lt;/p&gt;

&lt;p&gt;That's it! I hope this guide helps you. Comments and feedbacks are always appreciated. Stay sane, developers :)&lt;/p&gt;

&lt;p&gt;(Cover image generated by &lt;a href="https://playgroundai.com/" rel="noopener noreferrer"&gt;playground.ai&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>database</category>
      <category>developer</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>ChatGPT Already Solved Your Problem. Be A Problem Definer, instead.</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Tue, 26 Sep 2023 17:24:13 +0000</pubDate>
      <link>https://dev.to/yogski/chatgpt-already-solved-your-problem-be-a-problem-definer-instead-431h</link>
      <guid>https://dev.to/yogski/chatgpt-already-solved-your-problem-be-a-problem-definer-instead-431h</guid>
      <description>&lt;p&gt;Charles Kettering, the Head of Research at General Motors one hundred years ago said, "A problem well-stated is a problem half-solved." &lt;br&gt;
Well, little did he know that a century later, a problem well-defined is problem solved thanks to ChatGPT.&lt;/p&gt;

&lt;p&gt;ChatGPT rose to prominence in less than a year and became actual disruption for many people. Some are delighted, some are frightened, others are skeptical. Now the hype is calming down and the dust are settled, we can see ChatGPT from better perspective and use it for our advantage.&lt;/p&gt;

&lt;p&gt;When talking about using ChatGPT for my own advantage, I always ask this question: "How smart is ChatGPT actually?"&lt;br&gt;
&lt;a href="https://www.forbes.com/sites/karlfreund/2023/01/30/chatgpt-massive-disruption/?sh=3f8305fb659f" rel="noopener noreferrer"&gt;Forbes said &lt;/a&gt;it is smart because it passes exams, creates beautiful essay, etc. However, the term "smart" has many interpretations. Sabrina, the owner of &lt;a href="https://www.youtube.com/@answerinprogress" rel="noopener noreferrer"&gt;Answer in Progress Youtube &lt;/a&gt;channel, has very interesting point of view. You can watch the full video:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/QrSCwxrLrRc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;tldr: ChatGPT is smart when dealing with common or standardized problems and less so when facing problems that require ingenuity, creativity, or critical thinking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So, the better we are at providing standardized problem to ChatGPT, the better the output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This aligns with core principle of decomposition in programming. Think program as list complex instructions that can be broken down into modules, that can be broken down again into functions, and so on. For me, being a problem definer means being excellent problem decomposer.&lt;/p&gt;

&lt;p&gt;This is general question submitted to ChatGPT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: show me best implementation of integer sorting in javascript

ChatGPT: *proceeds to show Quick sort, Merge sort, and Radix sort*
*puts some code*
*puts disclaimer that the performance depends on your use case*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I added constraints and optimization goal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: I have an array of positive integers. The integer elements are not necessarily unique. I have between 0 and 2000 in array length, inclusive. The integer elements are random with long-tail distribution. I want to minimize space complexity and maintain adequate time complexity. How can I optimize sorting algorithm for such case in javascript?

ChatGPT: Use counting sort. It's good for your case.
*puts counting sort*
*puts advantages related to constraints*
*does NOT put evasive disclaimer*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was blown away by that realization and adjust my coding style. I tell ChatGPT MY abstraction, MY structure, MY constraints, and it responded better than asking generic programming questions. Surely it can be improved and refined. It was weird though, like having a junior programmer with staff knowledge.&lt;/p&gt;

&lt;p&gt;So let's be a better problem definer :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB&lt;/em&gt;:&lt;br&gt;
I was dormant for almost a year for various reasons, and this is my first comeback post. It is just humble opinion from me and I know this community has more exciting use case. Please let me know if you have any other way of gaining advantage with ChatGPT.&lt;br&gt;
Cheers 🍻 &lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>Dealing with Enum Type in PostgreSQL</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Wed, 15 Dec 2021 06:14:42 +0000</pubDate>
      <link>https://dev.to/yogski/dealing-with-enum-type-in-postgresql-1j3g</link>
      <guid>https://dev.to/yogski/dealing-with-enum-type-in-postgresql-1j3g</guid>
      <description>&lt;p&gt;This is a brief guide for effectively understanding and working with enum type in Postgres: Why does it exist? What does it do? How to work with it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Enum Type Exist?
&lt;/h2&gt;

&lt;p&gt;There is boolean data type to define a binary column in PostgreSQL. Kind of yes/no switch. For example, &lt;code&gt;is_active&lt;/code&gt; column is commonly used to describe whether a record is active.&lt;/p&gt;

&lt;p&gt;But what if there are more than two options? Another data type is needed to handle the use case. That data type also has to be strict to prevent other values being written. Then enum comes to the rescue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does Enum Type Do Well?
&lt;/h2&gt;

&lt;p&gt;Enum type is best solution to describe state data. It basically define states of a record in PostgreSQL. For example, an e-commerce platform needs to track package delivery status. The package is either in packaging process, waiting for pick-up, in delivery, in transit, or already delivered to customer. It can be formalized as five states: &lt;code&gt;PACKAGING&lt;/code&gt;, &lt;code&gt;WAITING_PICKUP&lt;/code&gt;, &lt;code&gt;IN_DELIVERY&lt;/code&gt;, &lt;code&gt;IN_TRANSIT&lt;/code&gt;, and &lt;code&gt;DELIVERED&lt;/code&gt;. Enum can handle this, in a way that will be described below.&lt;/p&gt;

&lt;p&gt;Enum type has following behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is treated as type in PostgreSQL&lt;/li&gt;
&lt;li&gt;It applies to all tables in same schema&lt;/li&gt;
&lt;li&gt;It has static values&lt;/li&gt;
&lt;li&gt;Enum value is case-sensitive&lt;/li&gt;
&lt;li&gt;Enum value size is 4 bytes&lt;/li&gt;
&lt;li&gt;Each enum value is unique (duplicate is not allowed)&lt;/li&gt;
&lt;li&gt;Adding enum value requires ALTER operation&lt;/li&gt;
&lt;li&gt;Order of enum values matter when querying&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use Enum
&lt;/h2&gt;

&lt;p&gt;Use enum if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cheap constraint is preferrable&lt;/li&gt;
&lt;li&gt;Enum values won't change a lot in the future&lt;/li&gt;
&lt;li&gt;Enum values aren't too many. Personally, I'd say less than 10 as rule of thumb.&lt;/li&gt;
&lt;li&gt;Enum values have different priority&lt;/li&gt;
&lt;li&gt;Avoiding invalid value is necessary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, other solutions like reference table or even simple integer type will suffice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enum Operations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Enum Type
&lt;/h3&gt;

&lt;p&gt;In this article, delivery status example above will be used. Below is general query syntax to create enum type.&lt;/p&gt;

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

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PACKAGING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WAITING_PICKUP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s1"&gt;'IN_DELIVERY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IN_TRANSIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DELIVERED'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Rename Enum Type
&lt;/h3&gt;

&lt;p&gt;One can rename enum type by using following query:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;delivery_status_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Get Enum Values
&lt;/h3&gt;

&lt;p&gt;To get list of enum values, use following query&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;UNNEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enum_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;delivery_status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is the result after creating &lt;code&gt;delivery_status&lt;/code&gt; enum above&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foauxwzx39tryn6j04ued.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foauxwzx39tryn6j04ued.png" alt="postgresql enum query result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Enum Value
&lt;/h3&gt;

&lt;p&gt;As the product evolves, one realizes that package can be lost, broken, stolen, etc. New package state is needed. Let's name it &lt;code&gt;OTHERS&lt;/code&gt; to describe unexpected situations that may happen to a package. &lt;/p&gt;

&lt;p&gt;As of version 9.1, PostgreSQL support adding enum value to do just that. Just execute query below&lt;/p&gt;

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

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It is also possible to add value before or after specific value.&lt;/p&gt;

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

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt; &lt;span class="k"&gt;BEFORE&lt;/span&gt; &lt;span class="s1"&gt;'DELIVERED'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="s1"&gt;'IN_TRANSIT'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The result is the same, new value in enum type.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwellzo6y6hwrrvpvny1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwellzo6y6hwrrvpvny1p.png" alt="postgresql update add enum value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prior to version 9.1, things were more complicated. A popular workaround consists of three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rename enum type to something else&lt;/li&gt;
&lt;li&gt;Create new enum with correct type name and value&lt;/li&gt;
&lt;li&gt;Delete old enum type.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PACKAGING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WAITING_PICKUP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s1"&gt;'IN_DELIVERY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IN_TRANSIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DELIVERED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Change or delete enum value
&lt;/h2&gt;

&lt;p&gt;As of version 10, PostgreSQL support renaming enum value&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="s1"&gt;'PACKAGING'&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'PREPARING'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Other than that, PostgreSQL doesn't support changing or deleting specific enum value. The workaround is the same as above: rename old type, create new and correct type, and delete old type.&lt;/p&gt;

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

&lt;span class="c1"&gt;-- change PACKAGING to PREAPRING&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PREPARING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WAITING_PICKUP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s1"&gt;'IN_DELIVERY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IN_TRANSIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DELIVERED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- delete IN_TRANSIT value&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt; 
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PREPARING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WAITING_PICKUP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="s1"&gt;'IN_DELIVERY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DELIVERED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'OTHERS'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status_old&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After changing enum value &lt;code&gt;PACKAGING&lt;/code&gt; to &lt;code&gt;PREPARING&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqh9m6sy4av3vck8krmou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqh9m6sy4av3vck8krmou.png" alt="posgtresql change enum value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing &lt;code&gt;IN_TRANSIT&lt;/code&gt; value&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq53mpj7n4wlwkcl167b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq53mpj7n4wlwkcl167b.png" alt="postgresql remove enum value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete enum type
&lt;/h2&gt;

&lt;p&gt;To delete enum type and all of it values, execute following query&lt;/p&gt;

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

&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;delivery_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;




&lt;p&gt;Enum type is useful tool to ensure type safety in PostgreSQL. I personally use it to make sure system flow doesn't mess up. I find this short article is useful to remind me important things about enum in PostgreSQL. I hope it helps you as well, dear readers.&lt;/p&gt;

&lt;p&gt;If you find other tips to deal with enum types in PostgreSQL, please let me know in comments.&lt;/p&gt;

&lt;p&gt;Stay sharp and curious.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Data Wrangling with VSCode (Yes, Really!)</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Tue, 17 Aug 2021 23:39:34 +0000</pubDate>
      <link>https://dev.to/yogski/data-wrangling-with-vscode-yes-really-2bbf</link>
      <guid>https://dev.to/yogski/data-wrangling-with-vscode-yes-really-2bbf</guid>
      <description>&lt;p&gt;In this article, I'm going to show techniques and tips for transforming raw text data to standard format using VSCode. It's effective, quick, and dirty. Suitable for one-time wrangling.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftos9n8jd0w4k2pz092cl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftos9n8jd0w4k2pz092cl.png" alt="Data Wrangling with Visual Studio Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People who works around data sometimes get strange one-time requests. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hey, can you get this data real quick? Shouldn't be too hard, right? Appreciate it".&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It happended to me (as full-stack dev) few times, say: list of banks, list of loan rates, and so on. It's good thing that the data is available. It's bad thing that they are in PDF or HTML table. My system only works with CSV or JSON. So it is wrangling time!&lt;/p&gt;

&lt;p&gt;There are many tools available for wrangling data. Nearly all of them are sophisticated and packed with features. Too many features means too many resource needed. I don't want to open Jupyter Notebooks or Tableau just to add commas or parse words. I need my resource for that 10 tabs of Stack Overflow, you know. &lt;/p&gt;

&lt;p&gt;Finally, I resort to VSCode. It is software used by me and millions of people, has powerful editing features, and has various extensions. I can tailor it to wrangle data quickly.&lt;/p&gt;

&lt;p&gt;Now, I will show how to wrangle data from PDF or HTML and transform it to CSV or JSON, using only VSCode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills
&lt;/h3&gt;

&lt;p&gt;Wrangling data in VSCode comes with a caveat: you need to understand regular expressions a.k.a &lt;strong&gt;regex&lt;/strong&gt;. Don't be intimidated. Treat regex as a friend. Play around with regex tester &lt;a href="https://regexr.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and &lt;a href="https://cheatography.com/davechild/cheat-sheets/regular-expressions/" rel="noopener noreferrer"&gt;cheat-sheet&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Extensions
&lt;/h3&gt;

&lt;p&gt;Before going further, I recommend installing at least three extensions on VSCode.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rainbow CSV&lt;/strong&gt; : it highlights columns by colors, detects inconsistency in data, and provides RBQL (basically SQL for CSV file).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON to CSV&lt;/strong&gt; : it converts JSON format to CSV and vice-versa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prettify JSON&lt;/strong&gt; : it makes JSON easier to read.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Data Source
&lt;/h3&gt;

&lt;h4&gt;
  
  
  PDF
&lt;/h4&gt;

&lt;p&gt;I'm going to use list of registered lending fintech companies in Indonesia, downloadable &lt;a href="https://www.ojk.go.id/id/kanal/iknb/financial-technology/Documents/PERUSAHAAN%20FINTECH%20LENDING%20BERIZIN%20DAN%20TERDAFTAR%20DI%20OJK%20PER%2010%20JUNI%202021.pdf" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTML
&lt;/h4&gt;

&lt;p&gt;I choose Wikipedia list of national heroes of Indonesia as example. Available both in &lt;a href="https://en.wikipedia.org/wiki/National_Hero_of_Indonesia" rel="noopener noreferrer"&gt;English&lt;/a&gt; and &lt;a href="https://id.wikipedia.org/wiki/Daftar_pahlawan_nasional_Indonesia" rel="noopener noreferrer"&gt;Bahasa Indonesia&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  From HTML to CSV
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;First, note that the first output format is always CSV because of its similarity with natural structure of table (e.g columns, rows, header). 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75cbj838zzx9poz32laj.png" alt="Copying table from HTML document"&gt;
&lt;/li&gt;
&lt;li&gt;Open the &lt;a href="https://en.wikipedia.org/wiki/National_Hero_of_Indonesia" rel="noopener noreferrer"&gt;HTML data source&lt;/a&gt;. Then, copy text from HTML. On this case, I will copy the whole table. It is also useful to note which column is needed.&lt;/li&gt;
&lt;li&gt;On VSCode, create new untitled file &lt;code&gt;ctrl + N&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3zt45d28bd4zop05r2u.png" alt="Paste text into VSCode"&gt;
&lt;/li&gt;
&lt;li&gt;Paste the text. Now we have nice list. Next, we want to &lt;strong&gt;remove unnecessary lines&lt;/strong&gt;. Things like title, page number might get copied accidentally. It can be removed.&lt;/li&gt;
&lt;li&gt;Now we need to &lt;strong&gt;remove unusable columns&lt;/strong&gt;. For this case, column &lt;code&gt;Ref(s)&lt;/code&gt; is not unusable and meaningless. So we need to get rid of it. If you need to filter columns, there is a way to do it later.&lt;/li&gt;
&lt;li&gt;Access replace text menu &lt;code&gt;ctrl + H&lt;/code&gt;, then press &lt;code&gt;alt + R&lt;/code&gt; for &lt;strong&gt;regex search&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Now, what do these refs have in common? They have square brackets. Then, inside square bracket is number or letter. There are some possible regex. Try to approach it.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrr3oh6oyaybisc4x159.png" alt="Example of inaccurate regex"&gt;
The picture above with &lt;code&gt;\[[0-9]\]&lt;/code&gt; regex works for single digit, but failed for two or more digits and letters. I also prefer to remove trailing whitespaces.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ufrp73fklmskzb13f2v.png" alt="Example of accurate regex"&gt;
Going further, I acknowledged that &lt;code&gt;\[[0-9a-z]*\]&lt;/code&gt; works. &lt;code&gt;\t?\[..?.?\]&lt;/code&gt; also works (it removes whitespace as well). Then, replace with &lt;code&gt;''&lt;/code&gt; a.k.a nothing.&lt;/li&gt;
&lt;li&gt;Replace all occurences : &lt;code&gt;ctrl + alt + enter&lt;/code&gt;. Now the refs are no more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace characters that confuses CSV&lt;/strong&gt;, like comma (&lt;code&gt;,&lt;/code&gt;) or double-tick (&lt;code&gt;"&lt;/code&gt;). It is important to avoid mistakes when parsing CSV. No need for regex now.

&lt;ul&gt;
&lt;li&gt;Replace text &lt;code&gt;ctrl + H&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use text : &lt;code&gt;,&lt;/code&gt; , replace with &lt;code&gt;;&lt;/code&gt; or leave it empty&lt;/li&gt;
&lt;li&gt;Replace all occurences : &lt;code&gt;ctrl + alt + enter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repeat on other characters&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup header manually&lt;/strong&gt; to match standard CSV format. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1et80i3eoumrztpz2sk0.png" alt="Setup header"&gt;
This is my recommendation:

&lt;ul&gt;
&lt;li&gt;avoid spaces&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;snake_case&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add commas manually&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add proper separator&lt;/strong&gt;, in this case: comma. 

&lt;ul&gt;
&lt;li&gt;For this data, I am a bit lucky. Each data is consistently separated by tab or whitespace. Just need to convert whitespace to comma. This is also why removing unnecessary whitespace on step 7 is important. Extra commas will be confusing.&lt;/li&gt;
&lt;li&gt;In some cases, data is separated by spaces. It can be confusing, especially when the column is string with spaces. There are strategies to overcome it below.&lt;/li&gt;
&lt;li&gt;Replace text &lt;code&gt;ctrl + H&lt;/code&gt;, then press &lt;code&gt;alt + R&lt;/code&gt; for regex search option&lt;/li&gt;
&lt;li&gt;Use text : &lt;code&gt;\t&lt;/code&gt; , replace with &lt;code&gt;,&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace all occurences : &lt;code&gt;ctrl + alt + enter&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin47h257479y9sa4wpmv.png" alt="Highlighted CSV"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;et voila!&lt;/em&gt; Data should be ready by now. &lt;strong&gt;Save it&lt;/strong&gt; as &lt;code&gt;csv&lt;/code&gt; file. Rainbow CSV should automatically highlight it afterwards.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  From PDF to CSV
&lt;/h3&gt;

&lt;p&gt;The basic is similar to HTML data source&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy all data from PDF&lt;/li&gt;
&lt;li&gt;Paste into VSCode workspace&lt;/li&gt;
&lt;li&gt;Remove unnecessary lines&lt;/li&gt;
&lt;li&gt;Remove unusable columns&lt;/li&gt;
&lt;li&gt;Regex search and replace&lt;/li&gt;
&lt;li&gt;Remove special characters (commas, double-ticks, etc.)&lt;/li&gt;
&lt;li&gt;Setup header manually&lt;/li&gt;
&lt;li&gt;Add proper separators&lt;/li&gt;
&lt;li&gt;Save as CSV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive into action.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PDF data often returns a mess. Unnecessary lines, inconsistent line breaks, space separators, are all present here.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnej21il7zvyc1chh4162.png" alt="Paste PDF mess"&gt;
It is better to observe this pile of mess for a while. Also check with original PDF file to figure out "how did this line ended up here?"&lt;/li&gt;
&lt;li&gt;Check header and footer of each PDF page. Something like page number, repeated header, or repeated title might slip in. Clean them all.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Nomor&lt;/code&gt; or row number column has no value, so it can be removed.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofoz3bctvc2qaea51ev6.png" alt="Regex including newline"&gt;
Now there's a little trick. Normal number regex like &lt;code&gt;[0-9. ]+&lt;/code&gt; is not accurate since there are matches at other places. But I know number column is always at the beginning of row. So I add newline &lt;code&gt;\n&lt;/code&gt; to my number regex, then replace it with &lt;code&gt;\n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A lot of regex find-and-replace are required.

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;Android\ndan iOS&lt;/code&gt; with &lt;code&gt;Android dan iOS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;-\n&lt;/code&gt; (it means web based fintech, not mobile-based) with &lt;code&gt;Web\n&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;Konvensional\ndan Syariah&lt;/code&gt; with &lt;code&gt;Konvensional dan Syariah&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fortunately, there's no character that might confuse CSV. Move on to next step.&lt;/li&gt;
&lt;li&gt;Handling header might require domain knowledge. So I basically change the header into this: &lt;code&gt;platform_name, website, company_name, official_register, date_of_registration, lending_type, platform_type&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now here comes the fun part of adding separators. Remember that we &lt;em&gt;cannot&lt;/em&gt; change data, and regex find-and-replace &lt;em&gt;always&lt;/em&gt; replace pattern to constant value. Thus, the key is &lt;strong&gt;identifying constant values&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Many URLs begin with either &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;https&lt;/code&gt;, or &lt;code&gt;www&lt;/code&gt;. So, I can replace &lt;code&gt;http&lt;/code&gt; with &lt;code&gt;,http&lt;/code&gt; and &lt;code&gt;www&lt;/code&gt; (spaced) with &lt;code&gt;,www&lt;/code&gt;. Just add comma before replaced value. Now that's progress.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3n4pn4musm6zisjgpfym.png" alt="Multiple cursors"&gt;
&lt;/li&gt;
&lt;li&gt;Some URLs have no prefix, but have &lt;code&gt;.id&lt;/code&gt; suffix. Find it with &lt;code&gt;[a-z]+\.id&lt;/code&gt; regex, then press &lt;code&gt;alt + enter&lt;/code&gt; to have multiple cursors working on them. Move to leftmost string with &lt;code&gt;ctrl + ←&lt;/code&gt;, then add commas manually.&lt;/li&gt;
&lt;li&gt;All companies in the list begin with &lt;code&gt;PT&lt;/code&gt;. So, replace &lt;code&gt;PT&lt;/code&gt; (spaced) with &lt;code&gt;,PT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;All official registers begin with &lt;code&gt;KEP&lt;/code&gt; or &lt;code&gt;S&lt;/code&gt;. There are some inconsistencies about spacing and dash/minus character. So, replace &lt;code&gt;KEP ?-?–?&lt;/code&gt; with &lt;code&gt;,KEP-&lt;/code&gt;. Also replace &lt;code&gt;S ?[-?–?]+&lt;/code&gt; with &lt;code&gt;,S-&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lending type is either 'Konvensional', 'Syariah', or 'Konvensional dan Syariah'. Replace &lt;code&gt;Konvensional&lt;/code&gt; with &lt;code&gt;,Konvensional,&lt;/code&gt;. Replace &lt;code&gt;Syariah&lt;/code&gt; with &lt;code&gt;,Syariah,&lt;/code&gt;. Then replace &lt;code&gt;Konvensional,dan,Syariah&lt;/code&gt; with &lt;code&gt;Konvensional dan Syariah&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Official register ends with year. So I can replace &lt;code&gt;/2021&lt;/code&gt; to &lt;code&gt;/2021,&lt;/code&gt; and &lt;code&gt;/2020&lt;/code&gt; to &lt;code&gt;/2020,&lt;/code&gt; and so on.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcd9ciuwlikf58rg8fk1.png" alt="CSV Lint menu"&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;CSV Lint&lt;/code&gt; from &lt;code&gt;ctrl + shift + P&lt;/code&gt; or menu at bottom of VSCode window to check any incorrect row.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save as &lt;code&gt;csv&lt;/code&gt; file, and we're done!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Extra Miles
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Select Specific CSV Columns
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Rainbow CSV&lt;/code&gt; has RBQL feature, which works like SQL. It can be used to select specific columns if needed.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcd9ciuwlikf58rg8fk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcd9ciuwlikf58rg8fk1.png" alt="Query menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on query menu, a new tab will open.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnci6b5olk47k3e59wq3f.png" alt="Run query to select column"&gt;
&lt;/li&gt;
&lt;li&gt;Select columns like in SQL query, then click &lt;strong&gt;Run&lt;/strong&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z66sndnpbs1b6aqav97.png" alt="Query result"&gt;
&lt;/li&gt;
&lt;li&gt;The result appears in new tab. Save as new CSV using &lt;code&gt;ctrl + shift + s&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Save as JSON
&lt;/h4&gt;

&lt;p&gt;Here's the tips to get JSON from CSV file, completely within VSCode.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4gdpj9vf1ahe2v9fo43u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4gdpj9vf1ahe2v9fo43u.png" alt="Convert CSV to JSON"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;JSON to CSV&lt;/code&gt; extension. Open the CSV file, then press &lt;code&gt;F1 &amp;gt; Convert CSV to JSON&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Prettify JSON&lt;/code&gt; extension. Press &lt;code&gt;F1 &amp;gt; Prettify JSON&lt;/code&gt;. Now it looks good!
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3biyry23qfqk15pw1ypp.png" alt="Pretty JSON"&gt;
&lt;/li&gt;
&lt;li&gt;On Windows, there are trailing whitespace &lt;code&gt;\r&lt;/code&gt;. Simply find &lt;code&gt;\r&lt;/code&gt; and replace with empty string ``&lt;/li&gt;
&lt;li&gt;Save as JSON file. Do not save directly using &lt;code&gt;ctrl + S&lt;/code&gt;, it will overwrite the CSV file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Handle Separator Placing
&lt;/h4&gt;

&lt;p&gt;To summarize, here are tips and trick to handle difficult separator placing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilize constant value&lt;/li&gt;
&lt;li&gt;Utilize newline&lt;/li&gt;
&lt;li&gt;Utilize multiple cursors like &lt;code&gt;alt + enter&lt;/code&gt; when searching&lt;/li&gt;
&lt;li&gt;Utilize manual multiple cursors with &lt;code&gt;ctrl + alt + ↑&lt;/code&gt; or &lt;code&gt;ctrl + alt + ↓&lt;/code&gt;. Note that only maximum of 10000 parallel cursors are allowed on VSCode. &lt;/li&gt;
&lt;li&gt;Utilize CSV Lint&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I've been using this method quite often on various data source. Once, I wrangle 80-page PDF file and transform it to 40000-row CSV in about 20 minutes. Just keep in mind these three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Follow these steps in order:

&lt;ul&gt;
&lt;li&gt;Copy all data then paste it into VSCode workspace&lt;/li&gt;
&lt;li&gt;Remove unnecessary lines&lt;/li&gt;
&lt;li&gt;Remove unusable columns&lt;/li&gt;
&lt;li&gt;Regex search and replace&lt;/li&gt;
&lt;li&gt;Remove special characters (commas, double-ticks, etc.)&lt;/li&gt;
&lt;li&gt;Setup header manually&lt;/li&gt;
&lt;li&gt;Add proper separators&lt;/li&gt;
&lt;li&gt;Save as CSV&lt;/li&gt;
&lt;li&gt;Add or remove column (optional)&lt;/li&gt;
&lt;li&gt;Convert to JSON (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Be nimble on regex and its behaviors&lt;/li&gt;
&lt;li&gt;Don't be afraid to undo your steps when things get messy. Maybe your regex wasn't accurate enough. Go back and fix your regex. It will save you time rather than handling data manually. Manual wrangling should be exception, not mandatory step.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;I am Yogi Saputro, a full stack developer at Pinjammodal.id. I’m also passionate in building system, pipeline, and provide value to businesses. Check out my &lt;a href="https://github.com/yogski" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and &lt;a href="https://yogski.github.io" rel="noopener noreferrer"&gt;portfolio&lt;/a&gt; as well.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>tutorial</category>
      <category>productivity</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Optimizing Conditional Bulk Insert in Node.js + PostgreSQL</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Sun, 01 Aug 2021 08:25:32 +0000</pubDate>
      <link>https://dev.to/yogski/optimizing-conditional-bulk-insert-in-node-js-postgresql-26gd</link>
      <guid>https://dev.to/yogski/optimizing-conditional-bulk-insert-in-node-js-postgresql-26gd</guid>
      <description>&lt;p&gt;It is my journey of improving a bulk insert algorithm. It's a mouthful title, but definitely worthwhile read for this specific niche. &lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;If you're either : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working on ETL pipeline from various sources&lt;/li&gt;
&lt;li&gt;Scraping websites&lt;/li&gt;
&lt;li&gt;Aggregating data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;chances are you will need conditional bulk insert.&lt;/p&gt;

&lt;p&gt;I described term &lt;code&gt;conditional bulk insert&lt;/code&gt; as an operation where many records need to be inserted to DB at once, while ensuring there's no conflict on unique identifiers. &lt;/p&gt;

&lt;p&gt;In this article, I'm going to compare three approaches of conditional bulk insert for Node.JS and PostgreSQL:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Naive approach&lt;/li&gt;
&lt;li&gt;Filtered approach&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ON CONFLICT&lt;/code&gt; approach&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Basic Implementation
&lt;/h3&gt;

&lt;p&gt;For the purpose of demonstration, let's say there is a system for benefits program registration in country X. Data collection process is carried offline by field agents. Then, each agent upload applicants data to system at any particular time. Suppose that the system must check duplicate &lt;code&gt;national ID of X&lt;/code&gt;. Citizen of X can only be registered once by single &lt;code&gt;national ID&lt;/code&gt;. For simplicity, we will only store first data with unique &lt;code&gt;national ID&lt;/code&gt;. If a duplicate exists later, it will neither update or get inserted to database.&lt;/p&gt;

&lt;p&gt;Now we will determine best algorithm to insert data in such requirements.&lt;/p&gt;

&lt;p&gt;Software requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;node at least &lt;code&gt;v12.0&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;npm at least &lt;code&gt;v6.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL at least &lt;code&gt;v9.5&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Project Setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make new project folder, for example &lt;code&gt;mkdir bulk_insert_demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;go to directory: &lt;code&gt;cd bulk_insert_demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create new Node project: &lt;code&gt;npm init -y&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install necessary packages: &lt;code&gt;npm install pg-promise faker&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create new file &lt;code&gt;index.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create new table &lt;code&gt;benefit_recipients&lt;/code&gt; with query below:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- table definition&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;benefit_recipients&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;serial&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;national_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;person_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;benefit_amount&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Naive Approach
&lt;/h3&gt;

&lt;p&gt;Naive approach states that first we check for ID of a record. If an ID exists, we do nothing. Otherwise, we insert record with new, unique ID. &lt;/p&gt;

&lt;p&gt;Translating to Node.JS with &lt;code&gt;pg-promise&lt;/code&gt; package is simple. I use ES6 async-await style. Checking for &lt;code&gt;national_id&lt;/code&gt; is possible using &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;. &lt;code&gt;INSERT&lt;/code&gt; process is also straightforward. I put them all in classic &lt;code&gt;for&lt;/code&gt; because it works well for async function. Finally, I added some console for information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Naive approach: for every row, check if national_id exists in DB. If it doesn't, insert the row
 * @param {*} client pg-promise client
 * @param {*} bulkData data to insert as array of object
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;naiveBulkInsert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkIdExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT COUNT(*) FROM benefit_recipients WHERE national_id = $1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkIdExists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// needed loose 0, not strict 0&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
          INSERT INTO benefit_recipients (national_id, person_name, city, benefit_amount) 
          VALUES ($1, $2, $3, $4)`&lt;/span&gt;&lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;person_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;benefit_amount&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Parsing &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; products...\r`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\nSuccessfully insert &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records.\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;p&gt;I have to admit, this is the kind of algorithm I wrote when I'm early in my career. It popped to mind directly, simple, and readable. These are the advantages of naive approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Easy to implement&lt;/li&gt;
&lt;li&gt;Easy to add custom validation logic&lt;/li&gt;
&lt;li&gt;Can handle duplicates within data source as well as between source - database&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Disadvantages
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Runs a lot of queries (between &lt;code&gt;N&lt;/code&gt; and &lt;code&gt;2N&lt;/code&gt; where &lt;code&gt;N&lt;/code&gt; is data count). We'll see the performance later.&lt;/li&gt;
&lt;li&gt;Not ACID compliant, unless wrapped by transaction.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Filtered Approach
&lt;/h3&gt;

&lt;p&gt;Naive approach has drawback because it uses many queries. It is like using one truck to send only one box, then tell the truck to go back and forth to send all boxes. Send all boxes on one truck would be more sensible.&lt;/p&gt;

&lt;p&gt;That is the basic idea of filtered approach. Instead of checking &lt;code&gt;N&lt;/code&gt; times and inserting &lt;code&gt;0 to N&lt;/code&gt; times, why not create one query for all checking and one for inserting? More precisely, one search query for duplicates between data source and database, then one insert query for data source that is not in duplicates. &lt;/p&gt;

&lt;p&gt;The diagram below explains what I wish to achieve.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65alwzsimvxuxswy59dr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65alwzsimvxuxswy59dr.png" alt="Bulk insert explained using venn diagram" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For duplicate search, a &lt;code&gt;SELECT&lt;/code&gt; with &lt;code&gt;WHERE ... IN (...)&lt;/code&gt; clause will do. Then, the query result will serve as filtering criteria for data source. I did some &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt; here. The methods can be chained for sure, if that is preferrable. After that, I used &lt;code&gt;helper&lt;/code&gt; from &lt;code&gt;pg-promise&lt;/code&gt; method for inserting multiple records.&lt;/p&gt;

&lt;p&gt;I found out later that this approach didn't resolve duplicates within data sources, for example if a benefit applicant register more than once. So I fixed it, making internal duplicate check as first step. &lt;/p&gt;

&lt;p&gt;So, this is full method for filtered approach.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="cm"&gt;/**
 * Filtered approach:
 * - Remove internal duplicates 
 * - Search intersection of national_id between bulk data &amp;amp; DB.
 * - Then, filter out duplicates
 * - Finally, insert the rest into DB
 * 
 * @param {*} client pg-promise client
 * @param {*} bulkData data to insert as array of object
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterBulkInsert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Resolve duplicates in bulkData. Can be more complex than this.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniqueBulkData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputNationalIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uniqueBulkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listDuplicates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT national_id FROM benefit_recipients WHERE national_id IN ($1:csv)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="nx"&gt;inputNationalIds&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duplicatesArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listDuplicates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataToInsert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uniqueBulkData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;duplicatesArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;national_id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ColumnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;benefit_recipients&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataToInsert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successfully insert &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataToInsert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Run with only two queries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INSERT&lt;/code&gt; method is ACID compliant&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Disadvantages
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Doesn't handle duplicates within data source by default&lt;/li&gt;
&lt;li&gt;Has a lot of array manipulation (may be substituted with more complex queries)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;ON CONFLICT&lt;/code&gt; Approach
&lt;/h3&gt;

&lt;p&gt;I kept pushing forward to improve the query. It turned out that simple PostgreSQL clause &lt;code&gt;ON CONFLICT(...) DO NOTHING&lt;/code&gt; is the answer I need. It get rid of &lt;code&gt;SELECT&lt;/code&gt; query and array manipulation from previous approach. The code became simpler and more concise. However, note that columns in &lt;code&gt;ON CONFLICT(...)&lt;/code&gt; must be declared as &lt;code&gt;UNIQUE&lt;/code&gt; in table definition.&lt;/p&gt;

&lt;p&gt;The algorithm for third approach is as follows:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onConflictBulkInsert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ColumnSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;benefit_recipients&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onConflict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; ON CONFLICT(national_id) DO NOTHING RETURNING *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertOnConflict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bulkData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;onConflict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertOnConflict&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successfully insert &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Run with only ONE query&lt;/li&gt;
&lt;li&gt;ACID compliant&lt;/li&gt;
&lt;li&gt;Can handle duplicates within data source as well as between source - database&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Disadvantages
&lt;/h4&gt;

&lt;p&gt;none so far&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Setup
&lt;/h3&gt;

&lt;p&gt;For the demonstration purpose, I define some testing strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;national_id&lt;/code&gt; length and bulk data size are parameterized to ensure duplicate values. For example, suppose &lt;code&gt;national_id&lt;/code&gt; is a three digit number with 1000 possible values (0-999). If I create 750 samples, then according to &lt;a href="https://en.wikipedia.org/wiki/Birthday_problem" rel="noopener noreferrer"&gt;birthday problem principle&lt;/a&gt; there's &amp;gt;99% chance of &lt;code&gt;national_id&lt;/code&gt; duplicates.&lt;/li&gt;
&lt;li&gt;Tests are run iteratively in various scenarios. First, each test is run independently for small samples. Then, sample size will be increased accordingly. After that, there will also be test to run three algorithms sequentialy in random order to further measure performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For testing purpose, I created some functions: &lt;br&gt;
&lt;code&gt;generateSample()&lt;/code&gt; to create array of object with two parameters : sample size and &lt;code&gt;national_id&lt;/code&gt; length&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateSample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// national_id, center of this test, is defined with specific idLength&lt;/span&gt;
      &lt;span class="na"&gt;national_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;person_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cityName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;benefit_amount&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cleanTable()&lt;/code&gt; is used to delete all records from &lt;code&gt;benefit_recipients&lt;/code&gt;. It is used after inserting to database. Otherwise, there would be all duplicates, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// I don't use TRUNCATE because I will display number of deleted records&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteRecords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`DELETE FROM benefit_recipients
  WHERE created_at &amp;gt; (current_timestamp - interval '1 day')
  RETURNING *`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Deleted &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deleteRecords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; records.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;runNaiveTest()&lt;/code&gt;, &lt;code&gt;runFilteredTest()&lt;/code&gt;, and &lt;code&gt;runOnConflictTest()&lt;/code&gt;, each consists of timing, sample generation, and &lt;code&gt;INSERT&lt;/code&gt; execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runNaiveTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;naive_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;executing naive approach...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`creating &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; samples...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sample1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;naiveBulkInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sample1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;naive_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runFilteredTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;filter_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;executing filter approach...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`creating &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; samples...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sample2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;filterBulkInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sample2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;filter_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runOnConflictTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on_conflict_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;executing on conflict approach...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`creating &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; samples...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sample3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;onConflictBulkInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sample3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on_conflict_approach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, an adjustable &lt;code&gt;main()&lt;/code&gt; function. It runs specific test with defined iterations, sample size, &lt;code&gt;national_id&lt;/code&gt; length, and PostgreSQL client. In the example below, I will run sequential test of naive, filtered, and &lt;code&gt;ON CONFLICT&lt;/code&gt; approach, 10 times.&lt;br&gt;
If I want to test each approach independently, I can adjust it here and re-run the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pgp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_CLIENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iteration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// to create &amp;gt;99% duplicate guarantee, I use formula (samples) / (10^idLength) = 0.75&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// adjust desired test here&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;iteration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runNaiveTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runOnConflictTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runFilteredTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;idLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;cleanTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of codes above can be written as one &lt;code&gt;index.js&lt;/code&gt; file like I did, or spread into separate files with proper import. Then run &lt;br&gt;
&lt;code&gt;DB_CLIENT=postgres://theusername:thepassword@localhost:5432/db_name node index.js&lt;/code&gt;, change &lt;code&gt;DB_CLIENT&lt;/code&gt; value to correct connection string.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Result
&lt;/h3&gt;

&lt;p&gt;First, I tested each approach independently with 750 samples.&lt;br&gt;
This is the result.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz86an0c4r81z2xgsb23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz86an0c4r81z2xgsb23.png" alt="Bulk Insert Algorithm Comparisons for small samples" width="752" height="452"&gt;&lt;/a&gt;&lt;br&gt;
Well, I expected naive approach to be slow, but not THAT slow. It takes almost 2000 times slower than filtered and &lt;code&gt;ON CONFLICT&lt;/code&gt; approach. That number might vary due to various reasons. My database is on remote server, so network latency is a factor. One thing is certain: naive approach is much slower than others. First lesson learned: Never use naive approach. From this point on, I'll exclude naive approach from testing.&lt;/p&gt;

&lt;p&gt;On a side note, filtered approach is a bit faster than &lt;code&gt;ON CONFLICT&lt;/code&gt; approach. We'll see more from them.&lt;/p&gt;

&lt;p&gt;This time, I increase sample size to 7500 and adjust &lt;code&gt;national_id&lt;/code&gt; to 4 digits. This is the performance result.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzt9onilgv9odpf12y1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzt9onilgv9odpf12y1u.png" alt="Bulk Insert Algorithm Comparisons for medium samples" width="752" height="452"&gt;&lt;/a&gt;&lt;br&gt;
For 7500 samples, filtered approach is about 33% faster than 'ON CONFLICT' approach.&lt;/p&gt;

&lt;p&gt;Then, I increase sample size to 75000. This time, there's a surprise.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2zw120xr054k2vzwc88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2zw120xr054k2vzwc88.png" alt="Bulk Insert Algorithm Comparisons for big samples" width="752" height="452"&gt;&lt;/a&gt;&lt;br&gt;
Filtered approach takes much longer this time. It also didn't increase linearly from previous sample. On the other hand, &lt;code&gt;ON CONFLICT&lt;/code&gt; approach seems to scale well linearly as its execution time increase is consistent.&lt;/p&gt;

&lt;p&gt;This might be caused by array manipulation. On filtered approach, array manipulation is carried by Node.JS. &lt;code&gt;filter()&lt;/code&gt; and &lt;code&gt;map()&lt;/code&gt; functions, especially in nested fashion, is expensive. Plus, javascript is not considered fast. On bigger sample size, it breaks. That is not the case for &lt;code&gt;ON CONFLICT&lt;/code&gt; approach. The heavy lifting is done by PostgreSQL internal which has been optimized for relational algebra stuff. &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Key takeaways&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't use naive approach. It is really slow.&lt;/li&gt;
&lt;li&gt;Filtered approach breaks on bigger &lt;code&gt;INSERT&lt;/code&gt; size.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ON CONFLICT&lt;/code&gt; approach is fast, scales well, ACID-compliant, and fulfill the requirements well. Use it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Credit: Photo by &lt;a href="https://unsplash.com/@kendallhenderson?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Kendall Henderson&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/freight?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>node</category>
      <category>postgres</category>
      <category>testing</category>
    </item>
    <item>
      <title>Axios Async/Await with Retry</title>
      <dc:creator>Yogi Saputro</dc:creator>
      <pubDate>Tue, 09 Mar 2021 04:15:58 +0000</pubDate>
      <link>https://dev.to/yogski/axios-async-await-with-retry-4j5j</link>
      <guid>https://dev.to/yogski/axios-async-await-with-retry-4j5j</guid>
      <description>&lt;p&gt;If you have been coding javascript for a while, you'd probably have known about &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;. It is a famous JS library for making HTTP request. Whether you are back-end or front-end developer, Axios is essential to access API services out there.&lt;/p&gt;

&lt;p&gt;Alas, reliability is scarce even when we're in 2020s. Things happen, and your HTTP request might get stalled. So what do we do? In some cases, we will try again until we get the data we need. In those cases, equipping Axios with retry capability is necessary.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to show how to equip Axios with retry capability i.e resend HTTP request when server doesn't answer. First, the native way. Then, we're going to use some libraries.&lt;/p&gt;

&lt;p&gt;I feel the need to write this tutorial since most examples of Axios are written in &lt;code&gt;.then&lt;/code&gt; pattern. I'm accustomed to &lt;code&gt;async/await&lt;/code&gt; pattern since it feels more familiar (especially if you learn other OOP languages). Performance-wise, &lt;a href="https://stackoverflow.com/questions/54495711/async-await-vs-then-which-is-the-best-for-performance" rel="noopener noreferrer"&gt;both are internally equal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, let's dive in to our code. I'm going to make a Node.JS project. Feel free to use whatever javascript stuff to suit your needs. Also, make sure your dependencies are fulfilled. I already installed Node.JS and NPM for this tutorial.&lt;/p&gt;

&lt;p&gt;First, make new project using Terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;request-retry
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;request-retry
&lt;span class="nv"&gt;$ &lt;/span&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, install axios package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we're going to edit &lt;code&gt;index.js&lt;/code&gt; using code editor. I'm going to make HTTP request to &lt;a href="https://mock.codes" rel="noopener noreferrer"&gt;https://mock.codes&lt;/a&gt; and see if it responds.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Basic lorem12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://mock.codes/200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;myRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we will get this result&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;{ statusCode: 200, description: 'OK' }&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Things are okay, right? Now, I'm going to rig this HTTP request by setting an unreasonably low timeout. To do that, add &lt;code&gt;timeout&lt;/code&gt; in the request config. Let's check &lt;code&gt;index.js&lt;/code&gt; again and edit &lt;code&gt;myConfig&lt;/code&gt; so it looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Basic lorem12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="c1"&gt;// 10ms timeout so servers hate you&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I run &lt;code&gt;$ node index.js&lt;/code&gt; again, I'll probably get something like this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happened? Due to unfinished request, &lt;code&gt;req&lt;/code&gt; doesn't get data from server. Therefore, its value is &lt;code&gt;undefined&lt;/code&gt;. We can't get keys from &lt;code&gt;undefined&lt;/code&gt;, hence the error. &lt;/p&gt;

&lt;p&gt;It is time to implement Axios with retry capability. But before going any further, I want to make clear when referring to &lt;code&gt;retry&lt;/code&gt;, mostly we want to have control over two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many times we want to retry&lt;/li&gt;
&lt;li&gt;how long we want to wait for each trial&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two main ways to do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement directly&lt;/li&gt;
&lt;li&gt;Use package&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Direct Implementation
&lt;/h3&gt;

&lt;p&gt;This option means doing everything from scratch. It's not too difficult, though. It is suitable option when we just need few types of request in our code and adding package would burden the app.&lt;/p&gt;

&lt;p&gt;One simple approach is enveloping every request with loop. Now let's say I'm willing to retry 3 times and 50 miliseconds for each request. This is the example of working code.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;// amount of retries we're willing to do&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Basic lorem12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// we're willing to wait 50ms, servers still hate you&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://mock.codes/200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cannot fetch data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cannot fetch data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;myRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's quite long, but if we're not doing it often across one project, this solution fits nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Package
&lt;/h3&gt;

&lt;p&gt;There are times when we have to connect to many endpoints with different characteristics. In such circumstance, using package is justified. &lt;/p&gt;

&lt;p&gt;There are 3 famous packages that satisfy our needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/retry" rel="noopener noreferrer"&gt;retry&lt;/a&gt;, a general purpose retry operation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/axios-retry" rel="noopener noreferrer"&gt;axios-retry&lt;/a&gt;, most popular retry add-on for Axios&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/retry-axios" rel="noopener noreferrer"&gt;retry-axios&lt;/a&gt;, second most popular retry add-on for Axios&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will use &lt;code&gt;retry-axios&lt;/code&gt; since it provides easier implementation on &lt;code&gt;async/await&lt;/code&gt; pattern. Now don't forget to read on its &lt;a href="https://github.com/JustinBeckwith/retry-axios#readme" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. Also, don't forget to install it using Terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;retry-axios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example of working code.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nx"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;raxConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// number of retry when facing 4xx or 5xx&lt;/span&gt;
        &lt;span class="na"&gt;noResponseRetries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// number of retry when facing connection error&lt;/span&gt;
        &lt;span class="na"&gt;onRetryAttempt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Retry attempt #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentRetryAttempt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// track current trial&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="c1"&gt;// don't forget this one&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://mock.codes/200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;myRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of doing loop, I basically attach &lt;code&gt;retry-axios&lt;/code&gt; object to Axios. My part is handling config for the request. It is much simpler and delivers same result as direct implementation.&lt;/p&gt;

&lt;p&gt;That's it! Now we can make Axios more reliable in sending HTTP request.&lt;/p&gt;

&lt;p&gt;Do you find it useful, informative, or insightful ?&lt;br&gt;
Do you find mistakes in my tutorial ?&lt;br&gt;
Do you have any questions?&lt;br&gt;
Feel free to comment below 👇 and let me know. &lt;/p&gt;

</description>
      <category>axios</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>async</category>
    </item>
  </channel>
</rss>
