<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: hoaitx</title>
    <description>The latest articles on Forem by hoaitx (@hoaitx).</description>
    <link>https://forem.com/hoaitx</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%2F1021549%2F8e50a085-ff13-416f-8722-cfb1d6cb13b9.jpeg</url>
      <title>Forem: hoaitx</title>
      <link>https://forem.com/hoaitx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hoaitx"/>
    <language>en</language>
    <item>
      <title>Compilation of Libraries Supporting CLI Application Development for Node.js</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Tue, 21 Jan 2025 04:19:00 +0000</pubDate>
      <link>https://forem.com/hoaitx/compilation-of-libraries-supporting-cli-application-development-for-nodejs-4h5c</link>
      <guid>https://forem.com/hoaitx/compilation-of-libraries-supporting-cli-application-development-for-nodejs-4h5c</guid>
      <description>&lt;p&gt;Hello everyone. I wonder if anyone here has been using command-line applications (CLI)? If so, why did you choose it over a graphical user interface (GUI)? If I remember correctly, I have written a few articles about the process of creating some applications for myself. Honestly, for me, there are many cases where CLI proves to be much more useful.  &lt;/p&gt;

&lt;p&gt;Recalling my early days of switching from Windows to Linux, specifically Ubuntu. I have cursed countless times about this damn operating system. The interface is ugly, hard to use, and everything requires typing commands; how can anyone remember all those lines? It’s not like Windows is good. The interface is intuitive and easy to use. You just need to look to know where to click, everything is visible.  &lt;/p&gt;

&lt;p&gt;True to the saying, "what you hate, fate gives you". In the end, I became "addicted" to this command-line typing without even noticing. There must be a reason for this addiction; it helps me approach problems faster. Instead of moving the mouse around, I can just type, which is much quicker. Not to mention that many software applications are provided in CLI form, which in turn leads to the creation of management tools, allowing us to easily install our favorite applications with just a single command.  &lt;/p&gt;

&lt;p&gt;That was also the inspiration for me to tackle problems using CLI. Most of it is to help manage my blog. In the early days of exploring, there were so many new things that I didn’t know. It took a lot of time to learn while doing. Understanding that psychology, today I will compile some libraries that I know from my previous learning process. These libraries not only help you create quality applications but also make them more appealing to users.  &lt;/p&gt;

&lt;p&gt;Oh! All of these are Node.js libraries. If you are using other platforms like Go, Rust… I’m sure there are similar libraries. You just need to search based on the ideas of the libraries below.  &lt;/p&gt;

&lt;p&gt;Let's get started!  &lt;/p&gt;

&lt;h2&gt;
  
  
  Framework
&lt;/h2&gt;

&lt;p&gt;First, we need to talk about the framework; this is where you structure your CLI application. Imagine the framework helps shape, structure the code, and features to fit the nature of the CLI.  &lt;/p&gt;

&lt;p&gt;Notice that we often see command-line applications using a format like this:&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;mycli image &lt;span class="nt"&gt;--resize&lt;/span&gt; 512:512 /path/to/image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;mycli&lt;/code&gt; being the application name, &lt;code&gt;image&lt;/code&gt; is almost a function, and the flag &lt;code&gt;--resize&lt;/code&gt; is typically used to specify additional attributes. Finally, &lt;code&gt;/path/to/image&lt;/code&gt; points to the data that needs to be processed.  &lt;/p&gt;

&lt;p&gt;If you are familiar with Node.js, you will see that CLI is quite similar to the &lt;code&gt;node&lt;/code&gt; command, while the following parameters can easily be captured through the &lt;code&gt;process.argv&lt;/code&gt; variable.&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 image &lt;span class="nt"&gt;--resize&lt;/span&gt; 512:512 /path/to/image
// console.log&lt;span class="o"&gt;(&lt;/span&gt;process.argv&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'node'&lt;/span&gt;, &lt;span class="s1"&gt;'index.js'&lt;/span&gt;, &lt;span class="s1"&gt;'image'&lt;/span&gt;, &lt;span class="s1"&gt;'--resize'&lt;/span&gt;, &lt;span class="s1"&gt;'512:512'&lt;/span&gt;, &lt;span class="s1"&gt;'/path/to/image'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, just parsing &lt;code&gt;process.argv&lt;/code&gt; is sufficient to classify and call the corresponding functions with the used parameters. At that point, the application is no different from a CLI.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/yargs" rel="noopener noreferrer"&gt;yargs&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/commander" rel="noopener noreferrer"&gt;commander&lt;/a&gt; are two foundational libraries to help us parse parameters as mentioned above. They provide very basic functions to serve as a foundation for other libraries.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/oclif/oclif" rel="noopener noreferrer"&gt;oclif&lt;/a&gt; is one example. This tool helps us create powerful CLI applications by optimizing the workflow. &lt;a href="https://github.com/oclif/oclif" rel="noopener noreferrer"&gt;oclif&lt;/a&gt; defines everything, from the directory structure to the help commands generated for new features. All you need to do is focus on writing the logic. After a "build" step, oclif produces a complete CLI application, including usage instructions without requiring you to do many additional steps.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration File
&lt;/h2&gt;

&lt;p&gt;In an application, apart from the default configurations or configurations hardcoded in the code, sometimes user configuration is still needed.  &lt;/p&gt;

&lt;p&gt;For example, you provide the user with the ability to set the input/output paths for files after processing. This option needs to be saved somewhere for future use. The easiest way is to create a text file and store all the information in it. Or more professionally, you can use the &lt;a href="https://www.npmjs.com/package/cosmiconfig" rel="noopener noreferrer"&gt;cosmiconfig&lt;/a&gt; library.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/cosmiconfig" rel="noopener noreferrer"&gt;cosmiconfig&lt;/a&gt; is a library that automatically fetches configuration files into your application. cosmiconfig supports many formats such as .json, .yaml, .yml... After loading, cosmiconfig puts all the values into a variable that you can use in your application.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Input
&lt;/h2&gt;

&lt;p&gt;Input is an essential part of CLI applications; besides receiving user data through flags like &lt;code&gt;--resize&lt;/code&gt;, there are many other optimized ways.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/inquirer" rel="noopener noreferrer"&gt;inquirer&lt;/a&gt; is a library that provides many ways to receive user data. For example, displaying a text input box, yes/no questions, or select options... each time a certain command is typed.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;

&lt;p&gt;Have you ever used a CLI application that shows beautiful "loading" effects during interaction? Or lines of text with various colors to highlight important information? There are many libraries to help us achieve this.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/ora" rel="noopener noreferrer"&gt;ora&lt;/a&gt; is a library that creates beautiful "loading" effects. These effects are often used to signal to users that processing is underway and they need to wait. ora provides many different "spinning" shapes. Additionally, you can combine it with text to create continuous messages on the screen, indicating the progress of the ongoing process.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/visionmedia/node-progress" rel="noopener noreferrer"&gt;progress&lt;/a&gt; helps you create "downloading" effects using a combination of ASCII characters.  &lt;/p&gt;

&lt;p&gt;Sometimes you will need to draw a table to display information for output, &lt;a href="https://github.com/cli-table/cli-table3" rel="noopener noreferrer"&gt;cli-table3&lt;/a&gt; is very suitable for this. Or if you simply want to enclose content in "boxes", &lt;a href="https://github.com/sindresorhus/boxen" rel="noopener noreferrer"&gt;boxen&lt;/a&gt; is a perfect choice.  &lt;/p&gt;

&lt;p&gt;Additionally, you can combine with &lt;a href="https://github.com/chalk/chalk" rel="noopener noreferrer"&gt;chalk&lt;/a&gt; to style the text. This is a great library to change colors, add effects like "bold", "dim", "italic"... to highlight content. Or if you prefer a "rainbow" effect, &lt;a href="https://github.com/bokub/gradient-string" rel="noopener noreferrer"&gt;gradient-string&lt;/a&gt; is unbeatable.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Utils
&lt;/h2&gt;

&lt;p&gt;In addition to the main libraries mentioned above, there are still many other utility libraries that CLI can use.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/open" rel="noopener noreferrer"&gt;open&lt;/a&gt; helps us open something like images, web addresses, or even applications. This library proves useful when we need the assistance of an external application that the CLI cannot handle.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sindresorhus/clipboardy" rel="noopener noreferrer"&gt;clipboardy&lt;/a&gt; helps read data from the clipboard or issue commands to copy anything you want.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shelljs/shelljs" rel="noopener noreferrer"&gt;shelljs&lt;/a&gt; is used to run any other command-line programs through the shell.  &lt;/p&gt;

&lt;p&gt;And many other libraries that I cannot list all here.  &lt;/p&gt;

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

&lt;p&gt;Above is a compilation of some libraries that assist in creating a CLI application for Node.js. As a CLI enthusiast, I have researched and applied quite a few useful libraries in my applications. What about you? Are you using any additional tools? Please leave a comment for me and everyone to know. Thank you!  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>cli</category>
    </item>
    <item>
      <title>Using Cloudflare Tunnel to public Ollama on the Internet</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Wed, 15 Jan 2025 03:20:56 +0000</pubDate>
      <link>https://forem.com/hoaitx/using-cloudflare-tunnel-to-public-ollama-on-the-internet-40m</link>
      <guid>https://forem.com/hoaitx/using-cloudflare-tunnel-to-public-ollama-on-the-internet-40m</guid>
      <description>&lt;p&gt;Hello everyone. Tet is just around the corner, have you prepared anything for yourself and your family yet? It seems to me that as the year ends, everyone gets busier. Since the beginning of the month, the traffic to the blog has decreased significantly. Sometimes it makes me anxious because I don't know where my readers have gone. Maybe they are taking an early Tet break, or the chatbot is too strong, or it could be due to the content not being engaging enough anymore. 😥  &lt;/p&gt;

&lt;p&gt;I must admit that in these last few weeks, I have been in the mindset of a busy person, not having much time to write regularly. It could be due to the nature of the job, combined with many issues to handle, so I no longer have the mental space to relax. But it’s okay; today I successfully configured &lt;a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" rel="noopener noreferrer"&gt;Cloudflare Tunnel&lt;/a&gt; in conjunction with &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; to "public" an API endpoint on the Internet - something I couldn't do a few weeks ago. I thought many people would need this, so I decided to write an article about it right away.  &lt;/p&gt;

&lt;p&gt;At first, I intended to write a short post in the Threads section, but then I realized it had been too long since I wrote a lengthy article, so I changed my mind. Can you believe it? A long article can be condensed into just a few short lines. Conversely, a short article can easily be made "flowery" enough to turn into a lengthy piece that many might dread. So why should one strive to write longer?  &lt;/p&gt;

&lt;p&gt;Wow! If I didn't say it, no one might know the reason. Writing is a way for me to relieve stress. By writing, I can connect with my readers, share, chat, or weave in stories and lessons I have learned. In other words, writing serves both as a form of relaxation and a means to interact with everyone.  &lt;/p&gt;

&lt;p&gt;Since launching the short article section &lt;a href="https://2coffee.dev/en/threads" rel="noopener noreferrer"&gt;Threads&lt;/a&gt;, I never expected so many people would be interested in it. Oh, but to say I didn't expect would be an exaggeration because I did a lot of research before implementing this feature. "Coding" a feature isn't hard; the challenge lies in how to operate it. Threads must ensure that the frequency of posts isn't interrupted; if I write an article infrequently, would anyone even come back to check for updates? This inadvertently creates pressure on how to both gather and summarize interesting and prominent news for readers. Many days I got too busy and forgot to write, and sure enough, the next day I had to publish a make-up post to keep my credibility intact. 😆  &lt;/p&gt;

&lt;p&gt;I know that many people enjoy reading, and I am one of those who loves writing. Sometimes reading isn't always in the mindset of being "chased by a deadline," on the way to find a solution, or learning something new... I believe that for many people, reading is similar to writing: it is for relaxation. Relaxing while gaining knowledge and experience is indeed a two-for-one deal, isn't it? 😁  &lt;/p&gt;

&lt;p&gt;I've talked too much already; let's get to the main point. Today I successfully configured Cloudflare Tunnel along with Ollama to publicize an API endpoint on the Internet. From there, anyone can access it without being confined to the local server (localhost) anymore. After reviewing the documentation for Ollama, it turned out to be simpler than I thought!  &lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Tunnel &amp;amp; Ollama
&lt;/h2&gt;

&lt;p&gt;If you don't know about Cloudflare Tunnel, please refer back to the article &lt;a href="https://2coffee.dev/en/articles/add-a-tunnel-locally-tool-bring-local-server-to-the-internet" rel="noopener noreferrer"&gt;Adding a "Tunnel Locally" Tool - Bringing Local Servers to the Internet&lt;/a&gt;. This is a tool that helps us map local servers to the Internet, effectively turning your computer into a server that anyone with an IP address or domain name can access.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; is a tool that allows us to run some large language models (LLMs) on our computers with just a single command. It simplifies the installation and usage of models. The standout feature is that it supports APIs compatible with OpenAPI.  &lt;/p&gt;

&lt;p&gt;In a previous article, I mentioned creating a Tunnel through a six-step process - a bit lengthy, right? In fact, Cloudflare Tunnel has a much quicker startup process, requiring only the installation of &lt;code&gt;cloudflared&lt;/code&gt; and then using a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:11434  
...  
Your quick Tunnel has been created! Visit it at &lt;span class="o"&gt;(&lt;/span&gt;it may take some &lt;span class="nb"&gt;time &lt;/span&gt;to be reachable&lt;span class="o"&gt;)&lt;/span&gt;:  
https://tennis-coordination-korea-wv.trycloudflare.com  
....  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immediately, you will see a random address that &lt;code&gt;cloudflared&lt;/code&gt; has generated. It maps to the address &lt;code&gt;http://localhost:11434&lt;/code&gt; on your computer. When accessed from another machine at &lt;code&gt;https://tennis-coordination-korea-wv.trycloudflare.com&lt;/code&gt;, we see the same result as accessing &lt;code&gt;http://localhost:11434&lt;/code&gt; on the local machine.  &lt;/p&gt;

&lt;p&gt;The above is just an example of mapping any port on your machine to the Internet; for Ollama or many other tools, additional configuration for the hostname in the headers is required. In the &lt;a href="https://github.com/ollama/ollama/blob/main/docs/api.md" rel="noopener noreferrer"&gt;Ollama documentation&lt;/a&gt;, it instructs:&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;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:11434 &lt;span class="nt"&gt;--http-host-header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"localhost:11434"&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, try calling the API using the new URL. Note that you must run the &lt;code&gt;llama3.2&lt;/code&gt; model from Ollama beforehand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://tennis-coordination-korea-wv.trycloudflare.com/api/generate &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{  
  "model": "llama3.2",  
  "prompt": "Why is the sky blue?"  
}'&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wonderful! At this point, everything is done, and you have an API endpoint pointing to Ollama on the local server that anyone can access. However, if you have a domain in Cloudflare and want to maintain a fixed address like &lt;code&gt;api-ollama.2coffee.dev&lt;/code&gt;, you need to configure it according to the six steps.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping a Fixed Domain
&lt;/h2&gt;

&lt;p&gt;It's very simple; after completing step 4 in the article &lt;a href="https://2coffee.dev/en/articles/add-a-tunnel-locally-tool-bring-local-server-to-the-internet" rel="noopener noreferrer"&gt;Adding a "Tunnel Locally" Tool - Bringing Local Servers to the Internet&lt;/a&gt;, modify the contents of the config.yml file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tunnel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tunnel-uuid&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;credentials-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path/to/.cloudflared/.json&lt;/span&gt;  

&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-ollama.2coffee.dev&lt;/span&gt;  
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;  
    &lt;span class="na"&gt;originRequest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
      &lt;span class="na"&gt;httpHostHeader&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost:11434"&lt;/span&gt;  
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http_status:404&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cloudflared tunnel run &lt;span class="o"&gt;[&lt;/span&gt;tunnel-uuid]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this method can help you create an API address similar to OpenAI's ChatGPT, it has many limitations, such as depending on the machine configuration and the model being used. Ollama can only handle one query at a time, so making continuous or simultaneous requests will not be efficient.  &lt;/p&gt;

</description>
      <category>tunnel</category>
      <category>llm</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hello the Last Day of 2024, Let's see what we accomplished last year!</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Sat, 04 Jan 2025 07:57:03 +0000</pubDate>
      <link>https://forem.com/hoaitx/hello-the-last-day-of-2024-lets-see-what-we-accomplished-last-year-3i5n</link>
      <guid>https://forem.com/hoaitx/hello-the-last-day-of-2024-lets-see-what-we-accomplished-last-year-3i5n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/newyear"&gt;2025 New Year Writing challenge&lt;/a&gt;: Retro’ing and Debugging 2024.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hello! Another year is about to pass, just a year ago today, I was still struggling to outline plans and goals, and in a blink, here I am today, sharing with you what I have accomplished. It is even more meaningful to share this with the community &lt;a href="https://dev.to"&gt;Dev.to&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;But first, I will reveal some statistics of mine.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Statistics
&lt;/h2&gt;

&lt;p&gt;In 2024, I wrote an additional 56 new articles, totaling over 100,000 words. It's amazing! Even I can't imagine writing that much. I welcomed 89 new members. Especially after launching the anonymous comment feature, I received an additional 95 new comments across all articles. Additionally, I committed to maintaining a daily post in the &lt;a href="https://2coffee.dev/en/threads" rel="noopener noreferrer"&gt;Threads&lt;/a&gt; section for over 5 months. As of now, the number of short posts has reached 159, and it will continue to grow in the future.  &lt;/p&gt;

&lt;p&gt;It's a bit disappointing that the email newsletter sign-up feature flopped, recording only 4 subscribers. On the other hand, the number of people subscribing to new post notifications in the browser is 142. Of these, about 60 are active users (who frequently receive notifications of new posts).  &lt;/p&gt;

&lt;p&gt;So what about reader activity?  &lt;/p&gt;

&lt;p&gt;There have been over 77,000 views across all articles, along with more than 40,000 new users. The number of active users is over 44,000. This means about 4,000 readers return to the blog more frequently.  &lt;/p&gt;

&lt;p&gt;Can you guess which article was read the most? That would be &lt;a href="https://2coffee.dev/en/articles/using-gpt-4-for-free-with-github-copilot" rel="noopener noreferrer"&gt;Using GPT-4 for Free with Github Copilot&lt;/a&gt; with over 9.5k views. Next is the blog's "signature" article &lt;a href="https://2coffee.dev/en/articles/how-to-delete-a-pushed-commit" rel="noopener noreferrer"&gt;How to Delete a Commit That Has Been Pushed?&lt;/a&gt;. The most visited page is the homepage with over 12k views. The most impressive is the Threads page, which, despite being newly created, still made it to the "top" 9 most visited pages.  &lt;/p&gt;

&lt;p&gt;Compared to 2023, how did these figures grow or decline?  &lt;/p&gt;

&lt;p&gt;Fortunately, most metrics have increased. According to statistics from Google Analytics, the number of active users has increased nearly 73% compared to last year, with new users up by 60%. The only downside is that the average reading time for articles has decreased by 30%. I wonder if it’s because my writing has gotten worse, so no one bothers to read anymore, or if people are gradually losing patience with long articles 😅.  &lt;/p&gt;

&lt;p&gt;When it comes to traffic sources, users are still finding the blog through Google, which remains in first place, increasing by over 50%. One thing that shocked me is that the number of direct visitors has increased by nearly 190%. Wow! This means they are proactively visiting without needing to go through Google anymore.  &lt;/p&gt;

&lt;p&gt;Furthermore, the blog has shown signs of appearing in search results from AI-based search engines like ChatGPT, Google Gemini, and Bing...  &lt;/p&gt;

&lt;p&gt;What have I learned?  &lt;/p&gt;

&lt;p&gt;2024 was also the time I became interested in the concept of Serverless. I learned and worked more with this new architecture. Serverless helps me minimize costs while alleviating concerns about system operation.  &lt;/p&gt;

&lt;p&gt;I took an interest in Rust and learned it. During my learning process, I documented and created a series of articles about my self-learning journey. I'm glad to see a lot of interest. Although I still can't use it fluently, to be honest, it's very powerful. I hope to apply Rust soon in the near future.  &lt;/p&gt;

&lt;p&gt;With the help of a brother, I learned a lot about large language models, RAG, and Generative AI. I applied artificial intelligence in many features of the product while enhancing my work productivity. Notably, the blog has integrated many features such as smart search or related post suggestions to enhance user experience.  &lt;/p&gt;

&lt;p&gt;Finally, I &lt;a href="https://2coffee.dev/en/articles/tear-down-and-rebuild" rel="noopener noreferrer"&gt;"Tear Down and Rebuild"&lt;/a&gt; my blog, replacing it with a newer tech stack. I recounted this process in an article. So far, everything is working perfectly.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Reflecting on the Goals of 2024:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In 2024, I will maintain all activities like last year. I will continue learning, writing, and spreading motivation, enjoying the spirit of coffee &amp;amp; books.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's great that my enthusiasm and spirit have not diminished. I have learned many new things, many new technologies, and especially released an open-source personal note-taking app within 3 months. It's called &lt;a href="https://github.com/tonghoai/opennotas" rel="noopener noreferrer"&gt;OpenNotas&lt;/a&gt;. Simple, secure, focused on writing. Many articles you read originated from here 😄.  &lt;/p&gt;

&lt;p&gt;I have read many new books. Among them, I must mention notable literary works from Vietnam and abroad. They are very good! I cannot deny how important reading influences my writing skills.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not stopping there, I hope that in 2024 I will elevate my personal brand, making more people know about me, even more!  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have actively participated in social media, especially Facebook and recently Threads. I have been diligent about reading more articles, interacting more with everyone, and proactively making friends and exchanging with many people I hadn’t known before. Wow, I wonder if that’s enough to make people know about me more? Surely it's a little bit 😅.  &lt;/p&gt;

&lt;p&gt;Another memorable milestone is posting on Dev.to and receiving a lot of attention from everyone. I have over 6,000 followers now. Thank you all for your interest in me.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Goals for 2025
&lt;/h2&gt;

&lt;p&gt;In the new year, I will definitely maintain my old habits. At the same time, this is a challenging time to break through and harvest more results from the foundations I have laid for a long time. Thank you and wish everyone achieves all their goals in the new year 2025.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>newyearchallenge</category>
      <category>career</category>
    </item>
    <item>
      <title>Semantic Search Feature</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Tue, 17 Dec 2024 02:48:35 +0000</pubDate>
      <link>https://forem.com/hoaitx/semantic-search-feature-4lbh</link>
      <guid>https://forem.com/hoaitx/semantic-search-feature-4lbh</guid>
      <description>&lt;p&gt;Hi there, have you noticed that the autumn atmosphere has become clearer in Hanoi recently? The morning was cool, and the evening came with strong winds. But behind that was a busy week for me. I was focused on running the "deadline" for my company's project, and in the evening, I tried to complete the search function for my blog. This deadline was different from usual because it was the main feature for the year for the product. And as for the blog, the search function had to be completed sooner or later, and this was the perfect time to do it.&lt;/p&gt;

&lt;p&gt;Before switching to Fresh, my blog already had a search function. The way I did it back then was to use Postgres's fulltext-search. For those who don't know, before using Postgres, I also used redisearch for search. Generally speaking, Postgres gave better results, while redisearch was more complex. But in reality, my blog data wasn't that massive, so redisearch didn't have a chance to shine.&lt;/p&gt;

&lt;p&gt;When I switched to Fresh, AI was booming. Many people were talking about AI and what it could do. After completing the basic features and getting ready to work on the search function, I thought, "Why not try using AI?" So, I decided to "release" the new blog version without the search function.&lt;/p&gt;

&lt;p&gt;To create a search function with AI, I had to spend a lot of time researching and experimenting. I learned about how to implement it, how to use LLMs models, embeddings models, vector data types, how to convert data to vectors, and how to query...&lt;/p&gt;

&lt;p&gt;To put it simply, a vector is a finite set of numbers, like in mathematics. The number of elements in the set determines the size (dimension) of the vector. The larger the size, the more the vector can generalize the data it represents. To convert regular data (text, speech, images, etc.) to vectors, there are many ways, but thanks to the popularity of LLMs today, you can just put the data into an embeddings model, and it will give you vector data.&lt;/p&gt;

&lt;p&gt;Semantic search (semantic search) is different from traditional fulltext keyword search. Fulltext search is based on the amount of text characters entered to match and return the most relevant results. Meanwhile, semantic search is based on the content. Suppose your article is explaining how node.js works. When searching for the phrase "how node.js works", semantic search can find the article. On the other hand, fulltext search will try to find articles containing the words "node.js", "works", ...&lt;/p&gt;

&lt;p&gt;To query vector data, you need at least two steps. First, convert the query into a vector, then use the query functions. For example, with &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;pg-vector&lt;/a&gt; - a Postgres extension that supports vectors - there are query functions like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftinh-nang-tim-kiem-ngu-nghia-semantic-search_pg-vector-support.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftinh-nang-tim-kiem-ngu-nghia-semantic-search_pg-vector-support.webp" alt="pg-vector search" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see L2 distance, Cosine distance, L1 distance... as vector comparison methods. Depending on the use case, you choose the query type accordingly. For example, in the search problem, I chose the Cosine distance method - that is, the two vectors should have a similar shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftinh-nang-tim-kiem-ngu-nghia-semantic-search_flow.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftinh-nang-tim-kiem-ngu-nghia-semantic-search_flow.webp" alt="Flow" width="800" height="805"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, choose a suitable database. I'm using Turso as my main database. However, Turso is based on SQLite, which isn't optimized for vector data. Although they introduced an &lt;a href="https://turso.tech/blog/turso-brings-native-vector-search-to-sqlite" rel="noopener noreferrer"&gt;extension&lt;/a&gt; to support vectors, it's a bit complicated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;pg-vector&lt;/a&gt; is the opposite. It's widely used and is a Postgres extension. When it comes to Postgres, I think of &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;, which offers free usage. Supabase has pg-vector integrated, and activation is just a click away, making it a great choice.&lt;/p&gt;

&lt;p&gt;Next is choosing models. To save costs, I've been looking for free models from the start. I couldn't help but mention groq with its Completions API. However, groq doesn't have embeddings models, so I had to find another one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ollama.com/library/nomic-embed-text" rel="noopener noreferrer"&gt;nomic-embed-text&lt;/a&gt; is an embeddings model I found in Ollama's library. It can vectorize text. Additionally, Nomic provides a free embeddings API with limitations. However, I should remind you that Nomic isn't a multilingual model. It supports Vietnamese to a limited extent, so the generated vector might not be optimal for Vietnamese semantics.&lt;/p&gt;

&lt;p&gt;After preparing everything, it's time to write code to add vector data and search logic.&lt;/p&gt;

&lt;p&gt;First, convert the article content into a vector and store it in Supabase. Instead of converting the entire article content, I summarize the main content of the article before feeding it into nomic-embed-text. This helps remove unnecessary information and reduce the input token count for the model to process.&lt;/p&gt;

&lt;p&gt;Another note is that although these models have free APIs, they always come with limitations. Processing data for the first time is very expensive, as I have over 400 articles in both Vietnamese and English. A better approach is to run the Llama 3.2 3B and nomic-embed-text models locally. I use &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;The search logic is simple. Take the user's query -&amp;gt; pass it through nomic-embed-text to convert to a vector -&amp;gt; query cosin with the article vector and sort by the closest distance between the two vectors.&lt;/p&gt;

&lt;p&gt;However, if the user searches for keywords like node.js, javascript, etc., it's likely that semantic search won't return results because the data is too short, and the generated vector doesn't contain enough meaning, making the cosine distance too large. Therefore, to handle this case, I need to maintain a fulltext search mechanism. Fortunately, Supabase supports this type of search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;Looking back, it seems simple, but the most challenging part for me was the data preprocessing steps.&lt;/p&gt;

&lt;p&gt;An article usually conveys multiple ideas, including main and secondary content. Typically, searchers are only interested in the main content of the article, and they tend to search for related things. If I convert the entire article content into a vector, it will be "diluted" or "noisy" because the vector size is limited. I think that if I can remove secondary information and emphasize the main idea, the search will be more accurate. Imagine an article with 1500 words converted into a 1024-dimensional vector, compared to an article with only the main content of 500 words in the same vector. Which one represents the data more "clearly"?&lt;/p&gt;

&lt;p&gt;Users' search patterns are also hard to predict because everyone searches differently. Some people like to keep it short, while others like to write longer or provide context for their questions... Therefore, processing user input data is also a challenge. How can I convert it into a concise and relevant query that matches the search content on the blog?&lt;/p&gt;

&lt;p&gt;The quality of the AI model used is also an issue. Generally, the more trained a model is, the better it is, and commercial models come with quality assurance. However, to minimize costs, I'm currently using free LLMs models with limitations. Hopefully, one day I'll be able to integrate more powerful models to improve the search quality for my blog.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rag</category>
    </item>
    <item>
      <title>5 things I like about Deno</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Thu, 12 Dec 2024 10:15:07 +0000</pubDate>
      <link>https://forem.com/hoaitx/5-things-i-like-about-deno-2f72</link>
      <guid>https://forem.com/hoaitx/5-things-i-like-about-deno-2f72</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://deno.com/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; stable version was introduced about 3-4 years ago. At that time, it received quite a bit of attention because none other than Ryan Dahl, the creator of Node.js, was also the patron of Deno. Huh! You heard that right. Why would he create a new tool to compete with his own "child"?&lt;/p&gt;

&lt;p&gt;Ryan Dahl admitted that &lt;a href="https://2coffee.dev/en/articles/5-things-ryan-dahl-regrets-about-node-js" rel="noopener noreferrer"&gt;Node.js has critical weaknesses&lt;/a&gt;. Initially, Node.js was designed to focus on simplicity and flexibility. But over the years, everything has gone beyond control. Node.js has developed very powerfully, gaining more attention, and as people tried to pack everything into this rising star, it became more complicated than necessary.&lt;/p&gt;

&lt;p&gt;Deno was born to address the weaknesses of Node. However, at the time of its launch, it did not showcase its strengths. Its performance was even inferior to Node.js, not to mention the lack of npm support – which was one of Node's biggest advantages. This made things increasingly difficult.&lt;/p&gt;

&lt;p&gt;Being a curious person, I quickly experimented with some code snippets using Deno and realized the inconvenience regarding the library system. "Wow, it might take a long time for my favorite libraries to appear on this platform; everything looks both new and unfamiliar," I thought!&lt;/p&gt;

&lt;p&gt;Everything changed when I started &lt;a href="https://2coffee.dev/en/articles/tear-down-and-rebuild" rel="noopener noreferrer"&gt;"Tear Down and Rebuild"&lt;/a&gt; my blog. After many times of hesitating and pondering over technology choices, the name &lt;a href="https://fresh.deno.dev/" rel="noopener noreferrer"&gt;Fresh&lt;/a&gt; appeared. However, Fresh requires Deno as its runtime environment. Having no prior deployment experience but thinking "it's just a JavaScript runtime environment!" gave me more confidence. The next story is this article.&lt;/p&gt;

&lt;p&gt;Today, I will summarize 5 points that I feel "like" when working with Deno.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript support
&lt;/h2&gt;

&lt;p&gt;Deno natively supports TypeScript. This means you can run a &lt;code&gt;.ts&lt;/code&gt; file directly without going through a conversion step to &lt;code&gt;.js&lt;/code&gt; like other libraries usually do. Many people wonder if this is different from using a tool to run TypeScript code like &lt;a href="https://www.npmjs.com/package/ts-node" rel="noopener noreferrer"&gt;ts-node&lt;/a&gt;? The answer is definitely yes because ts-node requires TypeScript configuration files to work in complex cases, while Deno does not. The default support for TypeScript simplifies the process of running &lt;code&gt;.ts&lt;/code&gt; code directly.&lt;/p&gt;

&lt;p&gt;I often create scripts to handle some minor tasks for myself. Every time I create a new script, I always hesitate between choosing &lt;code&gt;.js&lt;/code&gt; or &lt;code&gt;.ts&lt;/code&gt;. And when Deno appeared, &lt;code&gt;.ts&lt;/code&gt; became the default choice. Even in Node projects, when I need to write a script to perform a quick task, I still prefer choosing &lt;code&gt;.ts&lt;/code&gt; and using Deno to execute that code.&lt;/p&gt;

&lt;h2&gt;
  
  
  No more multiple configuration files
&lt;/h2&gt;

&lt;p&gt;The more I engage with JavaScript/TypeScript projects in general or Node.js in particular, one thing that I have always wondered about or even found annoying is... there are too many configuration files. Each different project has files with different names. From &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;package-lock.js&lt;/code&gt;, and an additional &lt;code&gt;tsconfig.js&lt;/code&gt; if the project uses Typescript... Not to mention if I need to configure a few more things like webpack, vite, tailwind, postcss... oh my! In the beginning, due to being unfamiliar, I had to read to understand the significance and usage of each configuration file that appears in the project, reading while silently cursing, "How on earth can you create hundreds of configuration files like this?"&lt;/p&gt;

&lt;p&gt;When moving to Deno, I was amazed to find that there were no configuration files at all. That means you can run the project without any config – sounds surreal, right? If there is any, it is just a single &lt;code&gt;deno.json&lt;/code&gt; file. Deno cleverly eliminated many complicated configurations or placed them into a single &lt;code&gt;json&lt;/code&gt; file. What a relief to open a project without worrying about the appearance of a strange name again!&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for Web APIs and Node APIs
&lt;/h2&gt;

&lt;p&gt;Deno has been and is trying to support &lt;a href="https://docs.deno.com/api/web/" rel="noopener noreferrer"&gt;Web APIs&lt;/a&gt; as much as possible. Web APIs are a standardized set of APIs available in the browser. Good support for Web APIs can allow programs running in Deno to also run in the browser, following the write once – run anywhere paradigm. This opens up avenues for "Universal" libraries.&lt;/p&gt;

&lt;p&gt;If you have ever worked with Serverless, especially with &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt;, you would know that Workers are not completely compatible with Node.js, so using Node-specific libraries is likely to not work. On the other hand, if a library uses or is compatible with Web APIs, it runs smoothly. This also applies to Deno.&lt;/p&gt;

&lt;p&gt;Support for &lt;a href="https://docs.deno.com/api/node/" rel="noopener noreferrer"&gt;Node APIs&lt;/a&gt; allows Deno to run most "packages" on npm. Thus, you can freely use npm packages without worrying about compatibility anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  New security mechanism
&lt;/h2&gt;

&lt;p&gt;It's quite strange that there is an obvious fact that when starting a Node project, it will by default have all the user's permissions. This means the program can freely access files or execute commands in the system on behalf of the user. Quite dangerous, isn't it? Imagine if you accidentally "run" someone else's project without checking it first, and it scans all the data on your machine, sending it back to some server, or encrypting all files for ransom, then consider your life ruined, a mistake that cannot be corrected!&lt;/p&gt;

&lt;p&gt;Deno addresses this flaw by adding flags to request permissions when running applications. Permissions can include file access, internet access... If the program wants to access the file system or connect to the internet, it must at least request permission. For example:&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;deno run &lt;span class="nt"&gt;--allow-read&lt;/span&gt; &lt;span class="nt"&gt;--allow-write&lt;/span&gt; &lt;span class="nt"&gt;--allow-net&lt;/span&gt; index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many permissions that need to be "requested"; for more details, refer to &lt;a href="https://docs.deno.com/runtime/fundamentals/security/" rel="noopener noreferrer"&gt;Security and permissions | Deno Docs&lt;/a&gt;. The quickest way is to allow all permissions by using the &lt;code&gt;-A&lt;/code&gt; or &lt;code&gt;--allow-all&lt;/code&gt; flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance is continually improving
&lt;/h2&gt;

&lt;p&gt;I remember the early days when it was just introduced; Deno seemed to lag behind Node.js in terms of program performance. Specifically, benchmark tests showed the difference between Deno and Node when running the same program; Deno always came up short. At one point, &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;bun.sh&lt;/a&gt; suddenly appeared. Bun emerged as a phenomenon because it outperformed both Node.js in terms of performance. This made Deno seem even more dull.&lt;/p&gt;

&lt;p&gt;In reality, when using bun to run some Node applications, I encountered quite a few issues and even bugs. It seems bun is not ready for "production." So at that time, I concluded that Node is still the go-to choice for those who love stability.&lt;/p&gt;

&lt;p&gt;Recently, with the release of &lt;a href="https://deno.com/blog/v2.0" rel="noopener noreferrer"&gt;Deno 2.0&lt;/a&gt;, significant efforts have been made to improve performance and enhance the programming experience of this "black dinosaur." According to the latest documentation they released, Deno stands out in every aspect compared to the well-known names Node and bun.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2F5-dieu-toi-thich-o-deno_deno-perf-charts.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2F5-dieu-toi-thich-o-deno_deno-perf-charts.webp" alt="Deno Performance Charts" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Above are the points I like when working with Deno. There are a few more features, such as providing Deno Deploy for free to deploy applications. However, I still occasionally come across articles that "criticize" performance and the module system, as well as low compatibility with Node. These limitations have been addressed in the latest 2.0 version. My perspective on Deno has changed compared to when it was first launched, and I hope Deno will receive more attention from the community and make even more breakthroughs in the future.&lt;/p&gt;

&lt;p&gt;What about you? Have you used Deno? Do you have any praise or criticism regarding this JavaScript runtime environment? Please leave your comments below!&lt;/p&gt;

</description>
      <category>deno</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Using GPT-4 for Free Through Github Models (with Limitations)</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Mon, 25 Nov 2024 02:16:26 +0000</pubDate>
      <link>https://forem.com/hoaitx/using-gpt-4-for-free-through-github-models-with-limitations-2a44</link>
      <guid>https://forem.com/hoaitx/using-gpt-4-for-free-through-github-models-with-limitations-2a44</guid>
      <description>&lt;p&gt;As a social media user, it is not difficult to see posts asking if anyone wants to share a &lt;a href="https://openai.com/index/chatgpt-plus/" rel="noopener noreferrer"&gt;ChatGPT Plus&lt;/a&gt; account. By subscribing to the Plus version, you can use the latest and most powerful models, such as gpt-4, gpt-4o, or o1-preview... This shows that the demand for using these models is very high. Many users do so for research and study purposes. Or simply to experience something new, to see what is outstanding compared to what is already known.&lt;/p&gt;

&lt;p&gt;With a price of $20 a month, I am sure many people will hesitate. We have too many bills to pay in a month, and there are many more important things to spend on. Therefore, if someone is willing to "chip in" to share the costs, it would be great; the cost would be reduced exponentially. If five people share, each person only needs to pay $4. However, this also creates many new problems. Currently, ChatGPT limits the number of queries within a certain time frame. Therefore, if someone "accidentally" uses up all the queries, the next user must wait until the limit resets. Not to mention the financial risks of sharing with strangers. Thus, it is best to only share with people you know.&lt;/p&gt;

&lt;p&gt;Previously, I wrote an article about how to use GPT-4 for free through Github Copilot. But in the end, this was just an unofficial "trick," and I warned readers that there was a risk of their Copilot accounts being "flagged." As of now, it is uncertain whether this method still works, but readers should not follow it anymore. According to statistics, that article still has many readers. Wow! The allure of GPT-4 is remarkable!&lt;/p&gt;

&lt;p&gt;On the occasion that Github just public previewed &lt;a href="https://github.com/marketplace/models" rel="noopener noreferrer"&gt;Github Models&lt;/a&gt; for anyone with a Github account to access their models, including many models of GPT and other well-known models like Llama, Mistral... In this article, I will guide everyone on how to use GPT-4 for free in a way that is as similar to ChatGPT as possible. And of course, it is not as good as the official version, with many limitations included, but it is still worth experiencing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Models
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/github-models/prototyping-with-ai-models" rel="noopener noreferrer"&gt;Github Models&lt;/a&gt; is a feature that Github supports developers to use large language models (LLMs) for free for testing purposes - as they say. After completing the testing phase, users must deploy their applications with other providers because the testing models have many limitations in speed and context. However, if only used for personal purposes, it is acceptable.&lt;/p&gt;

&lt;p&gt;We all know the website &lt;a href="https://chat.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; is for everyone - especially free users - to chat with gpt-4o, 4o-mini... After a few exchanges, you will run out of queries and be "downgraded" to the gpt-3.5 model. &lt;a href="https://chat.com/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; saves the history of exchanges, allowing users to review past conversations. This is also the easiest way for users to interact with GPT models.&lt;/p&gt;

&lt;p&gt;If you are not a developer, or perhaps are a developer but have not noticed, &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; - the company behind ChatGPT provides an &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;API&lt;/a&gt; to interact with their models. Simply put, instead of using the web chat, you can completely call the API to do the same thing. For example, a conversation in the API would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.openai.com/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "gpt-4o",
    "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "Hello!"
      },
      {
        "role": "assistant",
        "content": "Hi there! How can I assist you today?"
      },
      {
        "role": "user",
        "content": "What is the capital of Vietnam?"
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see that an API call includes all the content of the conversation. This means that to continue the conversation, you need to send back all the previous content between you and GPT. This explains that it is the context of the conversation. GPT does not store state, so the best way to help it understand the content being discussed is to resend all previous content.&lt;/p&gt;

&lt;p&gt;The special thing is that by calling the API, you can use the latest models without necessarily subscribing to the $20 GPT Plus package. However, API calls cost money, and the billing is very different from the web version. You pay more if you call the API more often, and the length of the conversation also incurs more costs since the API charges based on input/output tokens. Not to mention, the input/output costs differ depending on the model you use.&lt;/p&gt;

&lt;p&gt;"Wow, this is really confusing. Just calculating the costs is already so complicated that it might be easier to just subscribe to Plus 😆." Surely many people will think like that after reading this, but it's true! You spend money to buy satisfaction; that is the pinnacle of sales art. If you spend a little time and get to use it for free, then it forces us to read and experiment more.&lt;/p&gt;

&lt;p&gt;Below is the pricing table based on input/output tokens for the gpt-4o mini model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_gpt-4o-pricing.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_gpt-4o-pricing.webp" alt="gpt-4o mini pricing" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Input tokens are the number of characters sent, while Output tokens are the number of characters that the model responds with. This means that the more you send or the more the model responds, the more money you spend. Tokens are calculated based on a few principles; basically, you can think of them as roughly equivalent to words (1 token = 3/4 word), meaning 3 words will correspond to 3 tokens. A more standard formula can be found at &lt;a href="https://platform.openai.com/tokenizer" rel="noopener noreferrer"&gt;Tokenizer | OpenAI Platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://github.com/signup" rel="noopener noreferrer"&gt;registering an account&lt;/a&gt; on Github, access &lt;a href="https://github.com/marketplace/models" rel="noopener noreferrer"&gt;Marketplace Models&lt;/a&gt; and you will see a list of models available for use. Click on any model, for example, here is &lt;a href="https://github.com/marketplace/models/azure-openai/gpt-4o-mini" rel="noopener noreferrer"&gt;OpenAI GPT-4o mini&lt;/a&gt;. Click on the Playground button at the top right of the screen. Here you will see a chat interface very similar to ChatGPT. Try starting a conversation as usual. Congratulations, you are now using the gpt-4o mini for free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_gpt-4o-mini-playground.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_gpt-4o-mini-playground.webp" alt="gtp-4o mini Playground" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, this method is a bit inconvenient as it does not save the conversation history, and the interface is not very user-friendly. Next, I will guide you through a much more "professional" way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lobe Chat
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/lobehub/lobe-chat" rel="noopener noreferrer"&gt;Lobe Chat&lt;/a&gt; is an open-source project that provides an interface and features to interact with large language models. Simply put, Lobe Chat provides a chat interface similar to ChatGPT.&lt;/p&gt;

&lt;p&gt;Lobe Chat helps us manage conversations. In addition, it also tries to integrate many other features, but in the scope of this article, I will not mention them yet. Lobe Chat interacts with LLMs via API, so you need to configure the connection for calls to the corresponding model server. Here I will guide you to configure for Github Models.&lt;/p&gt;

&lt;p&gt;First, you need to obtain Github Tokens for basic authentication. Go to &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;Personal access tokens | Github&lt;/a&gt;. Click on "Generate new token (classic)." Enter a name for the token and choose the expiration time for the token as you wish. You do not need to select any scopes. Click on Generate token at the bottom and save the token you just received.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_github-tokens.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_github-tokens.webp" alt="Github Tokens" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, install Lobe Chat on your computer. You can deploy Lobe Chat to many cloud services like Vercel, Zeabur... with just one click because Lobe is a web-based application. Or if using Docker, simply run one command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3210:3210 &lt;span class="nt"&gt;--name&lt;/span&gt; lobe-chat lobehub/lobe-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access &lt;code&gt;http://localhost:3210&lt;/code&gt; to see the interface of Lobe Chat. Next, we need to set up the API configuration for it to work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-welcome.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-welcome.webp" alt="Lobe Chat Welcome" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the Lobe Chat avatar at the top left, select "Settings," click on the "Language Model" tab, and you will see the OpenAI setup screen. You need to fill in "API Key," "API Proxy Address," and "Model List." The API Key is the Github token you just created above, and the API Proxy Address at the time of writing this article is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://models.inference.ai.azure.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the "Model List" box, enter the names of the models you want to use. To know the exact name of the model, visit each model on Github Models, click on Playground, and switch to the "Code" tab. For example, here I enter "gpt-4o," "gpt-4o-mini." Click on the "Check" button to verify if the configuration is correct. If "Check Passed" appears, it has been successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-settings.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-settings.webp" alt="Lobe Chat Settings" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's try to create a new conversation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-started.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_lobe-chat-started.webp" alt="Chattings" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wonderful!&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;There are a few notable limitations that you need to be aware of while using it.&lt;/p&gt;

&lt;p&gt;First is the limitation on chat speed and the number of API calls per day. Github labels each model to determine these limitations. For example, looking at the table below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_models-limits.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_models-limits.webp" alt="Models Limits" width="800" height="1073"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These labels are found in the details of the model. For example, for gpt-4o labeled "High," it means that in one day, you can only make 50 requests - equivalent to 50 questions, and you are limited to 10 requests per minute as well as a maximum of 2 requests concurrently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_label-limits.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fsu-dung-gpt-4-mien-phi-thong-qua-github-models_label-limits.webp" alt="Labels Limits" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, pay attention to the "Tokens per request" which is the amount of tokens in/out for each request. For example, with gpt-4o it is "8000 in, 4000 out," meaning the maximum number of words sent is 8000 words, while the number of words that the model responds with is only a maximum of 4000. Meanwhile, gpt-4o supports input tokens up to 131k and output up to 16k. Clearly, there are too many limitations. Hopefully, in the future, Github will "expand" these limits.&lt;/p&gt;

&lt;p&gt;You also cannot create images or audio. Lobe Chat only stores conversations on your computer, so you can only review the conversation created on your machine or switch to configuration on a cloud service.&lt;/p&gt;

&lt;p&gt;Although there are still many limitations, for those who want to experience or use it infrequently, this is a "lifesaver" solution. If you exhaust the limits of this model, you can switch to another model to continue using. Besides GPT, there are still many powerful models waiting for you to explore.&lt;/p&gt;

&lt;p&gt;Do you find this method interesting, or do you have any methods you want to share? Please leave your comments below the article. Thank you.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>llm</category>
      <category>github</category>
    </item>
    <item>
      <title>Introduction to hono.dev - Building a Serverless API Easily</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Tue, 24 Sep 2024 15:09:59 +0000</pubDate>
      <link>https://forem.com/hoaitx/introduction-to-honodev-building-a-serverless-api-easily-1ek1</link>
      <guid>https://forem.com/hoaitx/introduction-to-honodev-building-a-serverless-api-easily-1ek1</guid>
      <description>&lt;p&gt;&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express.js&lt;/a&gt; is certainly a library that any JavaScript/Node.js developer knows. It helps us build a REST API server quickly. Besides, there are many libraries and middleware created to be compatible and easily integrated into projects using express.js, making it increasingly popular and well-known.&lt;/p&gt;

&lt;p&gt;Starting with express.js is not difficult. Just go through a few steps to install the library and write a little code, then use the &lt;code&gt;node&lt;/code&gt; command to start the server. The deployment process is not much harder. Push the code to git, pull it back to the server, use a process management tool like pm2 to start it up, and that's it.&lt;/p&gt;

&lt;p&gt;This is a nearly obvious process to apply when writing and deploying a Node server. Even later, when working for many companies, the process of pushing code to git, pulling it back to the server, and then "pm2 restart" - which I jokingly call the 3P process to operate the server - is still the same.&lt;/p&gt;

&lt;p&gt;Until one day, my CTO mentioned Cloudflare Workers, a form of serverless. He said it could do this and that... a little bit each day, and it made me curious. Occasionally, I would read about it, but honestly, it was hard to understand. Although everyone knows the concept of serverless, its practical application is unknown.&lt;/p&gt;

&lt;p&gt;What is mentioned many times will also attract attention. I started reading about serverless more seriously, but I still didn't know how to deploy the code to serverless. Because serverless has no server, where do the push-pull-pm2 restart happen?&lt;/p&gt;

&lt;p&gt;It turned out that there was a separate process for deploying everything to serverless. Moreover, this process is even simpler than the traditional 3P. All you need to do is run the &lt;code&gt;deploy&lt;/code&gt; command on your machine. When that's done, all the code will be uploaded to the serverless server and be ready to run immediately.&lt;/p&gt;

&lt;p&gt;In this article, I won't discuss the process of deploying code to serverless. Before that, we need to know about a library that helps us build an API on serverless. Why? Because express.js and similar libraries cannot run on many serverless servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  hono.dev
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;hono.dev&lt;/a&gt; is a library similar to express.js, providing solutions for building APIs. But why hono? Look at Cloudflare Workers' simple endpoint guide below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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;ctx&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will respond with the phrase "Hello World!". If you want to add a POST method, you need to check the condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&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;data&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello, &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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="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;Too cumbersome and complex, an API server is not that simple, but a collection of dozens, hundreds of different endpoints. That's when hono shines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello &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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  

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

&lt;/div&gt;



&lt;p&gt;Isn't it quick and easy? &lt;/p&gt;

&lt;p&gt;Hono.dev is quite similar to express.js or &lt;a href="https://koajs.com/" rel="noopener noreferrer"&gt;koa.js&lt;/a&gt;. Hono focuses on simplicity, routing, and middleware. Using hono is similar to the REST API libraries you've been using for a long time.&lt;/p&gt;

&lt;p&gt;For example, a simple middleware to measure the request processing time:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
  &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Response-Time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hono is very lightweight, only 14kb for the &lt;code&gt;hono/tiny&lt;/code&gt; package, making it ideal for environments with many limitations like serverless. In traditional server applications, package size or library size doesn't matter because system resources are abundant and easy to upgrade. However, in serverless environments, resources are scarce, so finding fast and lightweight libraries is essential.&lt;/p&gt;

&lt;p&gt;You may have heard of the term "universal module" and "adapter", which are often used to describe a module that can work in multiple environments. From servers, browsers, or even serverless environments. To achieve this, universal modules utilize APIs supported by the environment to adapt and create adapters for us to choose from. Even if the project structure is good enough, we don't need to modify too much code to run in different environments.&lt;/p&gt;

&lt;p&gt;Hono supports many adapters, making it perfect for serverless environments. Some notable names include Cloudflare Workers, Vercel, Netlify, AWS Lambda... or even Service Worker in browsers.&lt;/p&gt;

&lt;p&gt;For example, this code snippet runs well in Cloudflare Worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Hono!&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to switch to Netlify's serverless environment, we need to change the adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsr:@hono/hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsr:@hono/hono/netlify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Hono!&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that since the serverless environment has different deployment methods, the best way to choose a suitable configuration for each environment is to refer to Hono's documentation.&lt;/p&gt;

&lt;p&gt;Hono's documentation is very simple and concise, and we can start building a project after just a few reads. Its own "homegrown" middleware is constantly being added to enhance the experience for developers, so we don't need to reinvent the wheel. Additionally, Hono is receiving attention from the community with impressive growth on Github.&lt;/p&gt;

&lt;p&gt;This is a short introduction to Hono and what it can do. In the next article, we'll deploy a serverless API server to Cloudflare Workers from scratch!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Few Useful Functions in Node.js Util Module</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Sun, 22 Sep 2024 16:02:34 +0000</pubDate>
      <link>https://forem.com/hoaitx/a-few-useful-functions-in-nodejs-util-module-226d</link>
      <guid>https://forem.com/hoaitx/a-few-useful-functions-in-nodejs-util-module-226d</guid>
      <description>&lt;p&gt;Node.js encompasses a range of components that come together to form a JavaScript runtime environment. In our series on Node.js Architecture - Introduction to Node.js, we explored the various components that make up Node.js and their respective functions.&lt;/p&gt;

&lt;p&gt;Within Node.js, there are numerous built-in modules - i.e., modules that are integrated from the outset. One such module is &lt;code&gt;util&lt;/code&gt;, which, in my opinion, deserves more attention. The &lt;code&gt;util&lt;/code&gt; module comprises a collection of small utility functions that can be helpful in certain situations. In this article, we will delve into some of these functions...&lt;/p&gt;

&lt;h2&gt;
  
  
  util.promisify and util.callbackify
&lt;/h2&gt;

&lt;p&gt;The callback is one of the earliest ways to handle asynchronous code. However, callbacks have several limitations, such as creating nested code and causing the infamous "callback hell." Occasionally, reading a piece of code written in a callback style can be overwhelming. Adding new functionality can be challenging, as it requires introducing an additional layer of logic.&lt;/p&gt;

&lt;p&gt;In Node.js, there is a utility function to convert asynchronous functions using the callback style to Promises. This function is useful when you want to transition to a Promise-based approach and then combine it with async/await to make your code more concise and easier to follow.&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;util&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;node:util&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;fs&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;node:fs&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;stat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callStat&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;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="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;`This directory is owned by &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Conversely, we have &lt;code&gt;util.callbackify&lt;/code&gt; to convert Promise-based functions back to callbacks.&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;util&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;node:util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fn&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world&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;callbackFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callbackify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;callbackFunction&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;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&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="nx"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  util.deprecate
&lt;/h2&gt;

&lt;p&gt;If you frequently use libraries, you may have noticed console messages indicating that a function is deprecated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oldFunction&lt;span class="o"&gt;()&lt;/span&gt; is deprecated. Use newFunction&lt;span class="o"&gt;()&lt;/span&gt; instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a notification that the &lt;code&gt;oldFunction&lt;/code&gt; is nearing the end of its support lifecycle and may be removed in the future. It is a common approach to remind developers that a function is approaching its "retirement" and to use an alternative function instead.&lt;/p&gt;

&lt;p&gt;In Node.js, there is a simple way to display this notification if you need to warn others that a function is nearing its end-of-life.&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;util&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;util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;oldFunction&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;This function is deprecated!&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;deprecatedFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deprecate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oldFunction() is deprecated. Use newFunction() instead.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply "wrap" the &lt;code&gt;oldFunction&lt;/code&gt; inside &lt;code&gt;util.deprecate&lt;/code&gt;. Each time &lt;code&gt;oldFunction&lt;/code&gt; is called, the warning message will appear in the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  util.types
&lt;/h2&gt;

&lt;p&gt;From ES6 onward, we have additional functions to check the type of data: boolean, array, object, etc.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;util&lt;/code&gt; module has a &lt;code&gt;types&lt;/code&gt; property that extends the capability to check data types.&lt;/p&gt;

&lt;p&gt;For instance:&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="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;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isPromise&lt;/span&gt;&lt;span class="p"&gt;(&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;resolve&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt; &lt;span class="c1"&gt;// true&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;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isRegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/abc/&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&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;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDate&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="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A comprehensive list is available at &lt;a href="https://nodejs.org/docs/latest/api/util.html#utiltypes" rel="noopener noreferrer"&gt;util.types | Node.js documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  util.isDeepStrictEqual
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;isDeepStrictEqual&lt;/code&gt; is the most efficient method to compare two objects and determine if they are identical.&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;util&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;util&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;obj1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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;obj2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDeepStrictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to these functions, the &lt;code&gt;util&lt;/code&gt; module provides many other utility functions, such as &lt;code&gt;util.styleText&lt;/code&gt; to format text output in the console, and &lt;code&gt;util.parseEnv&lt;/code&gt; to parse the contents of environment variables in &lt;code&gt;.env&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;For more information, refer to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/docs/latest/api/util.html" rel="noopener noreferrer"&gt;Node.js v22.8.0 documentation | Util&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using pm2 to Manage Node.js Applications</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Fri, 02 Aug 2024 10:40:07 +0000</pubDate>
      <link>https://forem.com/hoaitx/using-pm2-to-manage-nodejs-applications-5ep7</link>
      <guid>https://forem.com/hoaitx/using-pm2-to-manage-nodejs-applications-5ep7</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When I first encountered Node.js and also learned how to use Linux, &lt;a href="https://www.npmjs.com/package/nodemon" rel="noopener noreferrer"&gt;nodemon&lt;/a&gt; was a library that I often used to develop applications, as it would automatically "reload" the new code upon saving without needing to manually "kill" and restart the application, a feature now commonly referred to as "hot reload."&lt;/p&gt;

&lt;p&gt;After development comes deployment. While practicing running the application on the server, I struggled to figure out how to run it. If I used the conventional method of typing "node index.js" or even used nodemon, whenever I exited the terminal or disconnected from the server, the application would also "disappear." I understood that the application would exit if the connection to the server was lost. So what to do?&lt;/p&gt;

&lt;p&gt;It was only later that I learned that to keep the application running continuously, we need a process management tool. This is not only necessary for Node but for most other languages as well. For Node, the most mentioned name is probably &lt;a href="https://pm2.keymetrics.io/" rel="noopener noreferrer"&gt;pm2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since I discovered &lt;a href="https://pm2.keymetrics.io/" rel="noopener noreferrer"&gt;pm2&lt;/a&gt;, I have been able to easily deploy my applications. However, over time, as I worked on many projects and encountered other technologies like Docker, Kubernetes, etc., pm2 gradually became less essential, as those tools already integrated management capabilities.&lt;/p&gt;

&lt;p&gt;Recently, while maintaining several projects, most of which used pm2, I had to spend time revisiting the documentation on how to use it, as it had been a while since I last used it. At that time, I suddenly realized that there had been many changes during that period. Many new features had been added, or there were features that I was not aware of.&lt;/p&gt;

&lt;p&gt;But I must honestly say that pm2 is still a very powerful Node.js application management tool. Anyone working with Node should take the time to learn about it. Therefore, in today's article, I will highlight some of the main features of this tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PM2?
&lt;/h2&gt;

&lt;p&gt;pm2 is a daemon process manager that helps you manage and keep your applications always online.&lt;/p&gt;

&lt;p&gt;Installing pm2 is very simple, through npm which is integrated with Node.&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;pm2@latest &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you can start your Node application:&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;pm2 start app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application runs in the background, and to see all running applications:&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;pm2 list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most useful feature of pm2 is that it keeps the application running in the background, meaning that even if you exit the server, your application will still run.&lt;/p&gt;

&lt;p&gt;Most people know that when running a Node application, it only runs on one core of the CPU. If the computer is multi-core, Cluster mode will help distribute all processes to the remaining cores. For example, if the CPU has 4 cores and you want to run on all 4, it's very simple.&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;pm2 start app.js &lt;span class="nt"&gt;-i&lt;/span&gt; max
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;max&lt;/code&gt; indicates all cores will participate; if you want a specific number, replace &lt;code&gt;max&lt;/code&gt; with a number.&lt;/p&gt;

&lt;p&gt;pm2 has a logging mechanism that writes logs to files for later retrieval. This information includes commands printed to the console like &lt;code&gt;console.log&lt;/code&gt;. To view the logs:&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;pm2 logs 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;0&lt;/code&gt; is the id of the process, or you can replace it with the name of the process.&lt;/p&gt;

&lt;p&gt;To stop a process:&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;pm2 stop 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To restart after stopping:&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;pm2 start 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to restart:&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;pm2 restart 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to completely delete the application, ensuring it was stopped beforehand:&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;pm2 delete 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;pm2 start&lt;/code&gt; commands are discrete and singular. Imagine if instead of one, there were multiple Node processes that needed to start at the same time? That's when &lt;a href="https://pm2.keymetrics.io/docs/usage/application-declaration/" rel="noopener noreferrer"&gt;ecosystem&lt;/a&gt; becomes useful. The ecosystem is a mechanism that groups all applications into a configuration file and starts them with a single command.&lt;/p&gt;

&lt;p&gt;To create a configuration file, use the command &lt;code&gt;pm2 ecosystem&lt;/code&gt;, which generates a file &lt;code&gt;ecosystem.config.js&lt;/code&gt; like the following:&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apps&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;env_production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the configuration to fit your project and start it using:&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;pm2 start ecosystem.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the event of a server restart, &lt;code&gt;pm2&lt;/code&gt; does not automatically start, causing all applications to become inactive. To address this issue, use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 startup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But before that, you need to "commit" the processes to be started at startup with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pm2 save&lt;/code&gt; creates a "snapshot" of the currently running processes to restore them later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced PM2
&lt;/h2&gt;

&lt;p&gt;Pm2 has some relatively useful advanced features in certain cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pm2.keymetrics.io/docs/usage/process-actions/" rel="noopener noreferrer"&gt;RPC Function&lt;/a&gt; works by running a function via the command line. For example, there is a function &lt;code&gt;countActive&lt;/code&gt; that returns the number of people currently online.&lt;/p&gt;

&lt;p&gt;First, create a file &lt;code&gt;rpc.js&lt;/code&gt; with the content:&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;tx2&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;tx2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;tx2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;countActive&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;reply&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;num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Keep application online&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&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;pm2 start rpc.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, if you want to trigger the &lt;code&gt;countActive&lt;/code&gt; function, simply use:&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;pm2 trigger rpc countActive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pm2 also provides an API to manage pm2 via RESTFul API. Simply put, this means you can create a server to add/edit/delete other applications using pm2, done through calling the API. You can read more details at &lt;a href="https://pm2.keymetrics.io/docs/usage/pm2-api/" rel="noopener noreferrer"&gt;PM2 API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And a few other advanced features as well.&lt;/p&gt;

&lt;p&gt;Recently, pm2 has also launched the &lt;a href="https://pm2.io/docs/plus/overview/" rel="noopener noreferrer"&gt;pm2 plus&lt;/a&gt; service, which allows comprehensive monitoring of applications such as real-time monitoring interface, reporting, logging, and notifications... It can be seen that it is quite similar to APM (Application Performance Monitoring) applications. However, since there is no free package, I haven't had the chance to experience this service. If you have or are using it, please leave a comment at the bottom of the article!&lt;/p&gt;

</description>
      <category>node</category>
      <category>pm2</category>
    </item>
    <item>
      <title>I'm Under DDoS Attack</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Mon, 01 Jul 2024 06:10:18 +0000</pubDate>
      <link>https://forem.com/hoaitx/im-under-ddos-attack-2p5j</link>
      <guid>https://forem.com/hoaitx/im-under-ddos-attack-2p5j</guid>
      <description>&lt;p&gt;Since the moment I started building my website, I have always considered the possibility of it being targeted for destruction. There are various forms of attacks such as DDoS, spam, or attacks on certain security vulnerabilities... Do you think I have made any enemies that I should be worried about? Actually, no, I have never had any conflicts with anyone, but I can't escape the "watchful eyes" of these malicious actors on the internet. This is not the first website I have built, so paying attention to these unfriendly behaviors is not new to me.  &lt;/p&gt;

&lt;p&gt;Recently, my blog has been experiencing a higher frequency of DDoS attacks. In its nearly 3 years of existence, I have lost count of the number of attacks. The lighter ones would cause the server to "freeze," resulting in slow response times. The heavier ones would cause the server to completely crash, making it inaccessible. So far, there have been no significant damages, but it is always a hassle to deal with this mess. It's not like we always have a computer and internet access nearby.  &lt;/p&gt;

&lt;p&gt;DDoS attacks are not new, but their destructive power is extremely high. Whatever the reason may be for an attacker to decide to DDoS a website, they must find great satisfaction in seeing the website crippled.  &lt;/p&gt;

&lt;p&gt;In the past, I hosted everything on DigitalOcean (DO) with a modest server configuration that was stable enough for the current user load. Occasionally, I would encounter a minor DDoS attack that would bring the server down. Initially, DO would issue warnings that the server was using more than 70% or even 90% of the CPU without knowing the cause. The &lt;a href="https://uptimerobot.com/" rel="noopener noreferrer"&gt;UptimeRobot&lt;/a&gt; tool would send alerts that my website was inaccessible. Later, upon checking the access logs, I discovered that I was being DDoSed.  &lt;/p&gt;

&lt;p&gt;Most of the attack durations are very short, and during those times, I could only grit my teeth and wait until it ended, sometimes even occurring in the middle of the night. When I woke up the next morning and rebooted the server, everything would go back to normal.  &lt;/p&gt;

&lt;p&gt;People might think, why not take measures to protect against DDoS? My feeling about that would be :|, because I don't know how to effectively defend against it. Everyone thinks the first step would be to set up rate limits, and let me tell you, I did that. Each IP address is limited to only 10 requests per second. So why not upgrade the server then? Where do I find the money to do that? $6 a month may not be much, but it serves as a financial safety net and covers the normal traffic if there are no malicious actors. Then, should I install more monitoring tools? Do you think I can install anything on a Shared CPU 1GB, 20SSD server?...  &lt;/p&gt;

&lt;p&gt;Actually, I've thought a lot about this issue, but I just can't effectively block DDoS attacks. Remembering the &lt;a href="https://www.cloudflare.com/learning/ddos/what-is-layer-7/" rel="noopener noreferrer"&gt;OSI 7-layer model&lt;/a&gt; that most technology students are taught. We have Layer 7 - the topmost layer - the application layer. The rate limits that I applied were at Layer 7, and the software installed also operated at Layer 7... Layer 7 is the layer that directly interacts with your application or server. In simple terms, if an attacker can send an HTTP request to your server, they are attacking at Layer 7. And as you understand, your server will have to process their requests, whether it be simply rejecting the request, it still requires some CPU time to process. A single request might be simple, but what about thousands, hundreds of thousands of requests at once? As you can see, my blog crashes.  &lt;/p&gt;

&lt;p&gt;About 3 weeks ago, I wrote an article &lt;a href="https://2coffee.dev/en/articles/migrating-from-digitalocean-to-cloudflare-pages" rel="noopener noreferrer"&gt;migrating from DO to Cloudflare&lt;/a&gt;. Everything was carefully planned, starting with migrating the frontend, then the API, and eventually everything to avoid maintaining a centralized server like DO. Perhaps this article "ticked off" some individuals and they decided to see if the "new home" could withstand DDoS attacks.  &lt;/p&gt;

&lt;p&gt;The answer is yes, but only partially. After discovering that the main blog was no longer SSR, attackers quickly realized that the API calls were still going to DO, and they continued attacking the API, with the consequence being the same - it crashed.  &lt;/p&gt;

&lt;p&gt;Finally, I couldn't bear it anymore. Even with limited time, I had to perform a large migration to Cloudflare to put an end to these attacks.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Cloudflare (CF) - a name not unfamiliar to many. Years ago, many people knew it for its free proxy servers, free SSL, CDN, and caches... there were many interesting things there. In the beginning, I used Cloudflare but didn't fully understand its functionalities, or even after setting up my website, I found that the access was slower or the caching features were quite annoying. However, recently Cloudflare seems to have transformed, with clearer operations, more documentation, and a stronger community.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/learning/ddos/layer-3-ddos-attacks/" rel="noopener noreferrer"&gt;Cloudflare stops Layer 3, 4 DDoS attacks&lt;/a&gt;. This means it can block suspicious requests before they reach Layer 7, your application. CF acts as a shield for you. All you need to do is configure it to block all suspicious queries.  &lt;/p&gt;

&lt;p&gt;To be honest, CF has many features that I haven't fully understood. But first, let's focus on defending against DDoS attacks, we can explore the other features later. The simplest way to defend against DDoS is by transferring your primary domain to Cloudflare, enabling the proxy and setting up rate limits.  &lt;/p&gt;

&lt;p&gt;Rate limiting is a way to limit the number of accesses within a specified period of time. This is one of the simple yet effective methods to counter DDoS attacks. Suddenly, an IP continuously sending requests to your address, whether with a motive to cause destruction or "unintentionally" using a certain "loop" command that they laugh about when asked, "Sorry, I accidentally triggered a bug"... Well, this is quite a "bug," and if you can't "fix" it, let me help.  &lt;/p&gt;

&lt;p&gt;You might find this intrusive, weren't we talking about setting up rate limits on the server earlier, and it didn't work? Well, remember that Cloudflare acts as an intermediary for all requests before they reach your actual server, meaning it operates at Layer 7, and according to them, CF has the technology or the capability to counter DDoS at layers 3 and 4. In summary, setting up rate limits at CF will block a considerable number of requests before they reach your actual server.  &lt;/p&gt;

&lt;p&gt;Now, I will guide readers on how to configure rate limits to minimize DDoS attacks through Cloudflare. Please note that this is one of the methods I have used, and there are many other methods, so if you have a better approach, please leave a comment below the article.  &lt;/p&gt;

&lt;p&gt;First, of course, you need to register a Cloudflare account and set up the domain you want to protect against DDoS. Don't worry, Cloudflare will guide you through the process after successful registration.  &lt;/p&gt;

&lt;p&gt;Once the domain is activated, go to "Security" &amp;gt; "WAF" on the left navigation bar.  &lt;/p&gt;

&lt;p&gt;WAF, in simple terms, is Cloudflare's firewall, which allows you to configure, block, modify... user requests before they reach your actual server. Look to the right side, to reach your server, a request will have to pass through all these security layers of CF.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rate-limit-ruletoi-dang-bi-ddos_waf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rate-limit-ruletoi-dang-bi-ddos_waf.webp" alt="WAF" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right there on the screen, switch to the "Rate limiting rules" tab and click the "Create rule" button at the bottom.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rate-limit-rule.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rate-limit-rule.webp" alt="Rate limiting rules" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, give a name to the "Rule" and select the path for which Cloudflare will set the limit. For example, here I choose "/", meaning that all paths of the main domain will be protected.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rule.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_rule.webp" alt="Rule" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, set the limit by filling in the "Requests" and "Period" fields - the number of requests within a specified period, combining to form the rate limit. For example, here I choose 10 requests within 10 seconds for each IP address. If the limit is exceeded, users will receive an HTTP Status 429 error.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_request.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_request.webp" alt="Requests" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, save the configuration and try spamming your website to test if it's working properly.  &lt;/p&gt;

&lt;p&gt;If you're wondering how to determine the number of requests, I suggest you "experiment". Keep trying until you find the right number, as it depends on the number of requests your website receives. For example, if a page loads 20 requests in total, the limit should definitely be higher than 20. Not to mention 1 second, then 2 seconds... and then it continues to make "n" additional requests, or users continuously navigate your website, generating "m" requests within a certain period of time... In conclusion, this number depends heavily on your website, so try and find a reasonable number.  &lt;/p&gt;

&lt;p&gt;Finally, all the statistics about requests exceeding the limit will be displayed here. You can click on that "0" to view the details. Here, you can see that I haven't had any requests exceeding the limit in the past 24 hours.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_stats.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Ftoi-dang-bi-ddos_stats.webp" alt="Stats" width="800" height="143"&gt;&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>ddos</category>
      <category>security</category>
    </item>
    <item>
      <title>Moving - From DigitalOcean to Cloudflare Pages</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Wed, 19 Jun 2024 16:14:49 +0000</pubDate>
      <link>https://forem.com/hoaitx/moving-from-digitalocean-to-cloudflare-pages-1p5</link>
      <guid>https://forem.com/hoaitx/moving-from-digitalocean-to-cloudflare-pages-1p5</guid>
      <description>&lt;p&gt;If you're interested, you may know that I use DigitalOcean to host this blog, with a modest configuration of 1GB RAM and 20GB SSD, which is sufficient for the TechStack that I have chosen. I call it sufficient because it still performs well with the current traffic, but deploying with Docker sometimes creates storage issues for the images it generates. Docker is known as the "hard drive killer" when you have many Images, combined with CI/CD setup, it's a "devastating combo". Just think, a 20GB hard drive, without subtracting the operating system, how can continuous deployment be possible? Occasionally, the server reports that the hard drive is full and I have to go in and delete some files.  &lt;/p&gt;

&lt;p&gt;By chance, I came across &lt;a href="https://pages.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Pages&lt;/a&gt; which provides a solution for deploying various types of websites, such as Vue, React, Nuxt.js, Next.js... I was curious to see what it had to offer. I spent a whole week researching it. Finally, I decided to try migrating the two interface pages to see if it could be done.  &lt;/p&gt;

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

&lt;p&gt;According to Cloudflare, Cloudflare Pages is a JAMstack platform for user interface developers and website deployment. Pages focuses on developers as it offers many solutions such as Git integration to support continuous deployment (CI/CD), as well as the deployment speed and performance of the application through it.  &lt;/p&gt;

&lt;p&gt;Realizing the potential of Cloudflare, I could move the two Front-end pages: the admin control panel (AdminCP) and the blog interface. The AdminCP is built with Vue.js using SPA, while the blog is built with Nuxt.js using SSR. For the SPA, the resource consumption is not much. As far as I can tell, it only takes up a few MB of memory because it is deployed through Nginx. On the other hand, SSR takes up quite a bit of memory, I must say it is the most memory-consuming among the running services. Simply because it is deployed through a Node.js server, and Node.js consumes a lot of memory. Both Vue and Nuxt.js are supported by Pages, so I can easily migrate these two pages. But before migrating, it is necessary to evaluate the required features.  &lt;/p&gt;

&lt;p&gt;First is the admin panel page, since it is built with Vue and uses SPA, migrating it to Pages isn't too complicated. All that needs to be done is to change the environment variables to receive the configuration during build.  &lt;/p&gt;

&lt;p&gt;As for the blog interface page, I came up with an idea: instead of using SSR as it is currently, why not try converting it to SSG? This way, I can use a command to generate the website into static HTML files and upload them to any host that supports static pages, not just Cloudflare Pages. Moreover, the speed will be much faster compared to regular SSR because there is no need to query the database and generate HTML code for each visit. Thinking about it, I spent a whole week modifying the Nuxt code to work well with SSG mode.  &lt;/p&gt;

&lt;p&gt;Finally, earlier this week, I completed the basic process of moving the two interface pages to Pages. Of course, there are still a few bugs that need to be fixed, but currently, it fully meets the reading and search needs of everyone.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Moving
&lt;/h2&gt;

&lt;p&gt;I can save some resource costs for the admin panel and blog interface pages. Although it is not much, now I don't have to worry too much about server overload or any errors that may occur, as it can still function normally since Cloudflare has stored all the HTML.  &lt;/p&gt;

&lt;p&gt;The CI/CD process is shortened and less complicated. Previously, I needed to write many scripts to support this through Gitlab CI and Docker, but now, anytime I push code to Gitlab, it can build automatically.  &lt;/p&gt;

&lt;p&gt;I have switched DNS to Cloudflare to take advantage of their CDN infrastructure and data caching mechanism. The blog now has an impressive access speed.  &lt;/p&gt;

&lt;p&gt;Lastly, Web in the EDGE may become a trend in the future. Meaning you don't have to deploy a specific server to run a website, you can just run it through services like Pages. To learn more about this trend, readers can visit &lt;a href="https://deno.com/blog/the-future-of-web-is-on-the-edge" rel="noopener noreferrer"&gt;The Future of the Web is on the Edge&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Left to Do
&lt;/h2&gt;

&lt;p&gt;Although the majority of the migration process went smoothly, there are still some issues that need time to address.  &lt;/p&gt;

&lt;p&gt;The first is undiscovered or discovered but non-impactful errors. This issue only requires time to fix, or if readers discover any errors, they can leave a comment to inform me and I will fix them.  &lt;/p&gt;

&lt;p&gt;There are some features that become unnecessary or disabled after migrating to Pages, and they need to be removed to avoid confusion in the future.  &lt;/p&gt;

&lt;p&gt;Another issue is that there is no way to activate a notification after a successful or failed Page deployment. Hopefully, Cloudflare will soon add a notification feature or, at least, I will run a &lt;code&gt;curl&lt;/code&gt; command along with the &lt;code&gt;npm run generate&lt;/code&gt; command to send notifications to Telegram.  &lt;/p&gt;

&lt;p&gt;That's what's currently on the agenda. In the long run, it is to completely eliminate the need for a server and move all services to the cloud. Then I will be able to set up an automated system and not worry too much about infrastructure.  &lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Promise: How to Use and Some Notes!</title>
      <dc:creator>hoaitx</dc:creator>
      <pubDate>Sun, 09 Jun 2024 07:24:44 +0000</pubDate>
      <link>https://forem.com/hoaitx/promise-how-to-use-and-some-notes-2fik</link>
      <guid>https://forem.com/hoaitx/promise-how-to-use-and-some-notes-2fik</guid>
      <description>&lt;p&gt;It would be a glaring omission not to talk about Promise in JavaScript. In fact, there have been many articles written about Promise, which you can find through Google or occasionally come across in a programming-related community. But because Promise is an important concept and everyone has a different way of explaining it, I still decided to write this article.  &lt;/p&gt;

&lt;p&gt;When I first learned JavaScript, Promise was the most confusing thing. I thought I understood it and knew how to use it, but in reality, there were still many long and painful stumbles that taught me valuable lessons. I read many articles about Promise in both English and Vietnamese, and gradually everything started to make sense, helping me understand and use it correctly.  &lt;/p&gt;

&lt;p&gt;This article will not go into the concepts of Promise but rather touch on some notes and possible misunderstandings. It aims to provide readers with a general understanding and help them avoid some mistakes when writing code.  &lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Promise?
&lt;/h2&gt;

&lt;p&gt;A Promise is an object that represents a future result. In other words, a Promise represents the result of an asynchronous function.  &lt;/p&gt;

&lt;p&gt;A Promise has three states corresponding to the three possible outcomes of an asynchronous function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pending&lt;/code&gt; is the initial state, waiting for the result.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fulfilled&lt;/code&gt; is the successful state, with a result value.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rejected&lt;/code&gt; is the failure state, with an optional error value.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fpromise-cach-su-dung-va-mot-vai-luu-y_promise-flow.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic-img.2coffee.dev%2Fpromise-cach-su-dung-va-mot-vai-luu-y_promise-flow.webp" alt="Promise" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, let's create a Promise that takes a number &lt;code&gt;x&lt;/code&gt; and returns the &lt;code&gt;fulfilled&lt;/code&gt; state if &lt;code&gt;x&lt;/code&gt; is divisible by 2, and the &lt;code&gt;rejected&lt;/code&gt; state otherwise.&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;function&lt;/span&gt; &lt;span class="nf"&gt;isEven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x is not even&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In essence, a Promise represents a future result. In the above example, creating a Promise is not necessary because all the actions inside the &lt;code&gt;isEven&lt;/code&gt; function are synchronous. So, when should we use a Promise and what makes a function asynchronous?  &lt;/p&gt;

&lt;p&gt;What makes a function asynchronous? I have mentioned it in many articles. Some I/O tasks provided by asynchronous functions cannot immediately return a result; they depend on external factors such as hardware speed, network speed, etc. Waiting for these actions can waste a lot of time or cause serious bottlenecks.  &lt;/p&gt;

&lt;p&gt;For example, when making a GET request to the address &lt;code&gt;https://example.com&lt;/code&gt;, the processing does not simply depend on CPU speed anymore; it also depends on your network speed. The faster the network, the quicker you receive the result. In JavaScript, we have the &lt;code&gt;fetch&lt;/code&gt; function to send the request, and it is asynchronous, returning a Promise.&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="nf"&gt;fetch&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://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To handle the result of a Promise, we use &lt;code&gt;then&lt;/code&gt; and &lt;code&gt;catch&lt;/code&gt; for success and failure cases, respectively.&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="nf"&gt;fetch&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://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&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="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;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If after some processing time, &lt;code&gt;fetch&lt;/code&gt; is &lt;code&gt;fulfilled&lt;/code&gt;, the function inside &lt;code&gt;then&lt;/code&gt; will be activated. Otherwise, if &lt;code&gt;fetch&lt;/code&gt; is &lt;code&gt;rejected&lt;/code&gt;, the function inside &lt;code&gt;catch&lt;/code&gt; will be executed immediately.  &lt;/p&gt;

&lt;p&gt;Sometimes you may come across questions like predicting the result of the following example:&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="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;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;fetch&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://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&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="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;err&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="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;3&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;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This question is actually to test your understanding of asynchronous behavior in JavaScript. The result will be 1, 2, 3 instead of 1, 3, 2. This is because of the nature of asynchronous processing. Since the result of asynchronous behavior will be returned in the future, JavaScript thinks, "OK, this asynchronous function does not have an immediate result yet. Let it be and continue processing the following commands. When everything is done, check if it has a result." For more information on how asynchronous processing works, you can refer to articles on &lt;a href="https://2coffee.dev/bai-viet/lap-trinh-bat-dong-bo-la-gi-tai-sao-javascript-la-ngon-ngu-lap-trinh-bat-dong-bo" rel="noopener noreferrer"&gt;Asynchronous Programming in JavaScript&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Promise has some useful static methods for various use cases, such as &lt;code&gt;all&lt;/code&gt;, &lt;code&gt;allSettled&lt;/code&gt;, &lt;code&gt;any&lt;/code&gt;, and &lt;code&gt;race&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.all&lt;/code&gt; takes an array of Promises, returns a Promise, and is in the &lt;code&gt;fulfilled&lt;/code&gt; state when all Promises in the array are successful. Otherwise, it is in the &lt;code&gt;rejected&lt;/code&gt; state when at least one Promise in the array fails.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Promise.allSettled&lt;/code&gt; is similar to &lt;code&gt;Promise.all&lt;/code&gt; but always returns the results of all Promises in the array regardless of success or failure. Both &lt;code&gt;Promise.all&lt;/code&gt; and &lt;code&gt;Promise.allSettled&lt;/code&gt; are useful when you need to run multiple asynchronous functions immediately without caring about the order of the results.  &lt;/p&gt;

&lt;p&gt;On the other hand, &lt;code&gt;Promise.race&lt;/code&gt; returns the result of the Promise that is settled first, regardless of success or failure. &lt;code&gt;Promise.any&lt;/code&gt; returns the first fulfilled Promise in the array. &lt;code&gt;race&lt;/code&gt; and &lt;code&gt;any&lt;/code&gt; are suitable when you have multiple Promises that perform similar actions and need a fallback between the results.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Promise Replaces "Callback Hell"
&lt;/h2&gt;

&lt;p&gt;It is surprising to know that JavaScript used to not have Promise. Yes, you heard it right. This fact led Node.js to also not have Promise in its early days, and that is the biggest regret the creator of Node.js has. &lt;/p&gt;

&lt;p&gt;Previously, all asynchronous tasks were handled through callbacks. We would define a callback function to handle the result in the future.  &lt;/p&gt;

&lt;p&gt;For example, an early asynchronous request function was &lt;code&gt;XMLHttpRequest&lt;/code&gt;. It handled the result through a callback.&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;function&lt;/span&gt; &lt;span class="nf"&gt;reqListener&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&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;new&lt;/span&gt; &lt;span class="nc"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reqListener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&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;reqListener&lt;/code&gt; is a function that is called when there is a result from the request to &lt;code&gt;https://example.com&lt;/code&gt;. Handling asynchronous behavior with callbacks brought some troubles, including what is commonly known as "callback hell".  &lt;/p&gt;

&lt;p&gt;For example, an asynchronous function &lt;code&gt;fnA&lt;/code&gt; takes a callback function with two parameters: &lt;code&gt;data&lt;/code&gt; representing the successful result and &lt;code&gt;err&lt;/code&gt; representing the error. Similar functions include &lt;code&gt;fnB&lt;/code&gt;, &lt;code&gt;fnC&lt;/code&gt;, etc. If we want to combine these processing functions together, we need to nest them inside each other.&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="nf"&gt;fnA&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err1&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="nf"&gt;fnB&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err2&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="nf"&gt;fnC&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err3&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="p"&gt;....&lt;/span&gt;  
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When there are too many callbacks written like this, our code becomes a literal "callback hell". It becomes messy and hinders the reading and understanding process.  &lt;/p&gt;

&lt;p&gt;Promise was introduced to bring a new approach to handling asynchronous behavior. We still use callbacks, but we are able to limit the "hell" by connecting everything sequentially through &lt;code&gt;then&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fnA&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;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fnB&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fnC&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="k"&gt;catch&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="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;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, many asynchronous callback-based functions were rewritten using &lt;code&gt;new Promise&lt;/code&gt;. In Node.js, we even have a built-in module called &lt;a href="https://nodejs.org/api/util.html#utilpromisifyoriginal" rel="noopener noreferrer"&gt;util.promisify&lt;/a&gt; specifically for this transformation. Callbacks are still supported, but with the benefits that Promise brings, many new libraries are using Promise as the default for handling asynchronous behavior.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Promise in Loops
&lt;/h2&gt;

&lt;p&gt;There are many unfortunate mistakes when using Promise without fully understanding its essence and one of them is handling sequential asynchronous operations in a loop.  &lt;/p&gt;

&lt;p&gt;Suppose you need to iterate through 5 pages, making paginated API calls from 1 to 5 and concatenate the return values in order into an array.&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;results&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;1&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="mi"&gt;5&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://example.com?page=&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="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;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code creates a loop to fetch data from page 1 to page 5, and the returned data is pushed into the &lt;code&gt;results&lt;/code&gt; array. At first glance, the result would be an array of data in the order from the first page to the last page. However, in reality, each time it runs, you will find that the data in &lt;code&gt;results&lt;/code&gt; is randomly ordered.  &lt;/p&gt;

&lt;p&gt;Remember &lt;code&gt;fetch&lt;/code&gt;, it returns a Promise, representing a future result... While &lt;code&gt;for&lt;/code&gt; is trying to loop through as quickly as possible, you can think of it as all 5 &lt;code&gt;fetch&lt;/code&gt; commands being called and started "almost instantly". At this point, CPU speed is less important - it's the network speed that determines which &lt;code&gt;fetch&lt;/code&gt; command has the first result. As soon as it has the result, it immediately pushes it into &lt;code&gt;results&lt;/code&gt;, resulting in the data being added randomly.  &lt;/p&gt;

&lt;p&gt;To solve this issue, there are several ways. For example:&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nf"&gt;fetch&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://example.com?page=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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="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;data&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;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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://example.com?page=2&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="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;data&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;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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://example.com?page=3&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is crazy, and nobody writes code like this. Imagine what if there were 1000 pages? It's a joke, but the example above attempts to illustrate the idea of how to wait for the previous request to complete before proceeding to the next request.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/bluebird" rel="noopener noreferrer"&gt;Bluebird&lt;/a&gt; is a very good Promise library that provides many utility functions to make working with asynchronous functions easier.  &lt;/p&gt;

&lt;p&gt;The above example can be rewritten using the &lt;code&gt;each&lt;/code&gt; function provided by Bluebird.&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&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;each&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&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="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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://example.com?page=&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="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;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Async/Await - The Missing Piece of Promise?
&lt;/h2&gt;

&lt;p&gt;To create a Promise, we use the syntax &lt;code&gt;new Promise&lt;/code&gt;, but with async/await, we simply declare an &lt;code&gt;async&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isEven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x is not even&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Promise uses &lt;code&gt;then&lt;/code&gt; to handle the return result at some point in the future, async/await is as simple as using &lt;code&gt;await&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="nf"&gt;fetch&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://example.com&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;resultJSON&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting from Node.js version 14.8, we have the top-level await feature, which means &lt;code&gt;await&lt;/code&gt; calls are no longer limited to inside an &lt;code&gt;async&lt;/code&gt; function; they can be used outside as well. Before that, &lt;code&gt;await&lt;/code&gt; could only be used inside an &lt;code&gt;async&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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://example.com&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;resultJSON&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While Promise uses &lt;code&gt;.catch&lt;/code&gt; to handle errors, async/await uses &lt;code&gt;try...catch&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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://example.com&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;resultJSON&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="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;err&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;err&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;It is evident that async/await allows us to write asynchronous code as if it were synchronous, without callbacks and without the need for &lt;code&gt;then&lt;/code&gt;. We simply wait for the result using &lt;code&gt;await&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;However, this can also lead to some dangerous misunderstandings, such as forgetting the &lt;code&gt;await&lt;/code&gt; keyword to wait for the result of an asynchronous behavior or mistakenly thinking that an asynchronous function is a synchronous one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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://example.com&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;resultJSON&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resultJSON&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;err&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;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getData&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;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In essence, &lt;code&gt;getData&lt;/code&gt; returns a Promise, so the above program does not work correctly if the intention is to get data from an API call. To fix this, we need to change it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getData&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;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  return vs. return await
&lt;/h2&gt;

&lt;p&gt;Sometimes you may come across code that 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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fn&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;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;asyncFn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;fn&lt;/code&gt; is returning an &lt;code&gt;await&lt;/code&gt; of the asynchronous function &lt;code&gt;asyncFn&lt;/code&gt;. The author probably intended for &lt;code&gt;fn&lt;/code&gt; to always return the result of &lt;code&gt;asyncFn&lt;/code&gt;, as &lt;code&gt;await&lt;/code&gt; is waiting for the result of the asynchronous function, effectively turning &lt;code&gt;fn&lt;/code&gt; into a synchronous function, or in other words, "not" returning a Promise.  &lt;/p&gt;

&lt;p&gt;Unfortunately, this is a dangerous misunderstanding. In essence, &lt;code&gt;await&lt;/code&gt; is only meant to be used at the top-level or inside an &lt;code&gt;async&lt;/code&gt; function, and if it is &lt;code&gt;async&lt;/code&gt;, it must return a Promise. Therefore, &lt;code&gt;fn&lt;/code&gt; always returns a Promise. So why use &lt;code&gt;return await asyncFn()&lt;/code&gt;?  &lt;/p&gt;

&lt;p&gt;Simply using &lt;code&gt;return asyncFn()&lt;/code&gt; can save you a bit of keystrokes, as there is no significant difference compared to &lt;code&gt;return await asyncFn()&lt;/code&gt;. The big change happens when there is a &lt;code&gt;try...catch&lt;/code&gt; in &lt;code&gt;return&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Let's consider the following two functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rejectionWithReturnAwait&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saved!&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rejectionWithReturn&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="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;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saved!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first function, &lt;code&gt;rejectionWithReturnAwait&lt;/code&gt;, is intentionally using &lt;code&gt;return await&lt;/code&gt;, and when this Promise is rejected, the &lt;code&gt;catch&lt;/code&gt; block quickly catches the error and executes the &lt;code&gt;return 'Saved!'&lt;/code&gt; statement. This means the function returns a Promise containing the string 'Saved'.  &lt;/p&gt;

&lt;p&gt;In contrast, &lt;code&gt;rejectionWithReturn&lt;/code&gt;, without &lt;code&gt;await&lt;/code&gt;, is attempting to reject a &lt;code&gt;Promise.reject(new Error())&lt;/code&gt;, and the &lt;code&gt;catch&lt;/code&gt; block is never executed.  &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;Promise - Mozilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/blog/release/v14.8.0" rel="noopener noreferrer"&gt;Top-level await - Node.js v14.8.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>promise</category>
    </item>
  </channel>
</rss>
