<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shin'ya Ueoka</title>
    <description>The latest articles on DEV Community by Shin'ya Ueoka (@ueokande).</description>
    <link>https://dev.to/ueokande</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%2F43113%2F9ff446c4-1e79-42b1-92e6-0d69832fbeff.png</url>
      <title>DEV Community: Shin'ya Ueoka</title>
      <link>https://dev.to/ueokande</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ueokande"/>
    <language>en</language>
    <item>
      <title>Migrating node-fetch/form-data to Node.js native APIs</title>
      <dc:creator>Shin'ya Ueoka</dc:creator>
      <pubDate>Sat, 27 Jul 2024 12:31:04 +0000</pubDate>
      <link>https://dev.to/ueokande/migrating-node-fetchform-data-to-nodejs-native-apis-2j7h</link>
      <guid>https://dev.to/ueokande/migrating-node-fetchform-data-to-nodejs-native-apis-2j7h</guid>
      <description>&lt;p&gt;The release of Node v16.15.0 and v17.5.0 brought in the &lt;a href="https://developer.mozilla.org/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/docs/Web/API/FormData" rel="noopener noreferrer"&gt;FormData&lt;/a&gt;. Before that, the &lt;a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer"&gt;node-fetch&lt;/a&gt; and &lt;a href="https://github.com/form-data/form-data" rel="noopener noreferrer"&gt;form-data&lt;/a&gt; packages were used to use browser-like APIs in Node.js. To migrate node-fetch/form-data packages to Node.js native APIs, some changes can be made by removing import statements of 3rd-party packages, but there are important considerations to keep in mind when using Node.js features such as file system. This article outlines about migrating from node-fetch/form-data packages to Node.js native APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of migration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; import fs from 'node:fs';
&lt;span class="gd"&gt;-import FormData from 'form-data';
-import fetch from 'node-fetch';
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-const file = fs.createReadStream('secret.txt');
&lt;/span&gt;&lt;span class="gi"&gt;+const file = await fs.openAsBlob('secret.txt', { type: 'text/plain' });
&lt;/span&gt; const form = new FormData();
&lt;span class="gd"&gt;-form.append('file', file);
&lt;/span&gt;&lt;span class="gi"&gt;+form.append('file', file, 'secret.txt');
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; const response = await fetch('https://example.com/upload', {
   method: 'POST',
   body: form,
 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Incompatibility of types
&lt;/h3&gt;

&lt;p&gt;The form-data package and Node.js FormData are not compatible and cannot be passed directly with the Fetch API. If you pass form-data to the native Fetch API, the request body will be the string &lt;code&gt;[object FormData]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FormData&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;form-data&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;form&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;FormData&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;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/upload&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// [object FormData]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The form-data package accepts &lt;code&gt;fs.ReadStream&lt;/code&gt; in Node.js. The native API accepts a browser-compatible &lt;a href="https://nodejs.org/docs/latest/api/globals.html#class-blob" rel="noopener noreferrer"&gt;Blob&lt;/a&gt;. To create a Blob from a file, we can sue &lt;code&gt;fs.openAsBlob()&lt;/code&gt;,added in Node.js v19.8.0. If you pass  &lt;code&gt;fs.ReadStream&lt;/code&gt; to the native API's FormData, the value will be &lt;code&gt;[object Object]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret.txt&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;form&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;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Content-Disposition: form-data; name="file"&lt;/span&gt;
&lt;span class="c1"&gt;// &lt;/span&gt;
&lt;span class="c1"&gt;// [object Object]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Incompatibility of internal behavior
&lt;/h3&gt;

&lt;p&gt;The form-data package obtains the file name from &lt;code&gt;fs.ReadStream&lt;/code&gt; and uses it as the &lt;code&gt;filename&lt;/code&gt; field (&lt;a href="https://github.com/form-data/form-data/blob/v4.0.0/lib/form_data.js#L218-L242" rel="noopener noreferrer"&gt;internal implementation&lt;/a&gt;). It also determines the file type from the file name and sets the Content-Type header (&lt;a href="https://github.com/form-data/form-data/blob/v4.0.0/lib/form_data.js#L244-L275" rel="noopener noreferrer"&gt;internal implementation&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Blob does not have a file name, it needs to be specified manually. The native API's FormData defaults to &lt;code&gt;application/octet-stream&lt;/code&gt; if the Content-Type is not specified. We can specify the Content-Type explicitly from the argument of &lt;code&gt;fs.openAsBlob()&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;file&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openAsBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret.txt&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;form&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;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Content-Disposition: form-data; name="file"; filename="blob"&lt;/span&gt;
&lt;span class="c1"&gt;// Content-Type: application/octet-stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openAsBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret.txt&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&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;form&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;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Content-Disposition: form-data; name="file"; filename="secret.txt"&lt;/span&gt;
&lt;span class="c1"&gt;// Content-Type: text/plain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Fetch API and FormData have become &lt;a href="https://nodejs.org/docs/latest-v22.x/api/globals.html#fetch" rel="noopener noreferrer"&gt;Stability: 2 (Stable)&lt;/a&gt; from Node.js v21.0.0. &lt;code&gt;fs.openAsBlob()&lt;/code&gt; is still &lt;a href="https://nodejs.org/docs/latest-v22.x/api/documentation.html#openasblob" rel="noopener noreferrer"&gt;Stability: 1 (Experimental)&lt;/a&gt; in the latest version of Node.js (v22.5.1 as of at July 27, 2024). We need to take care when running in old runtimes or using experimental APIs which has potential feature changes.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>Vimmatic - borwsing web pages with Vim-like key binds</title>
      <dc:creator>Shin'ya Ueoka</dc:creator>
      <pubDate>Mon, 10 Apr 2023 14:02:14 +0000</pubDate>
      <link>https://dev.to/ueokande/vimmatic-borwsing-web-pages-with-vim-like-key-binds-5f6e</link>
      <guid>https://dev.to/ueokande/vimmatic-borwsing-web-pages-with-vim-like-key-binds-5f6e</guid>
      <description>&lt;p&gt;Using a browser is a part of life these days.  Most Vim users have wanted to navigate a browser by Vim-like key binds.  Today, I will introduce a new browser extension, &lt;a href="https://github.com/ueokande/vimmatic" rel="noopener noreferrer"&gt;Vimmatic&lt;/a&gt;, which enables you to use your browser with Vim-like key binds.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Vimmatic supports both Firefox and Google Chrome.  You can get Firefox add-on or Chrome Extensions from the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;a href="http://godban.github.io/browsers-support-badges/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Falrra%2Fbrowser-logos%2Fmaster%2Fsrc%2Ffirefox%2Ffirefox_48x48.png" alt="Firefox"&gt;&lt;/a&gt;&lt;br&gt;Firefox&lt;/th&gt;
&lt;th&gt;
&lt;a href="http://godban.github.io/browsers-support-badges/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Falrra%2Fbrowser-logos%2Fmaster%2Fsrc%2Fchrome%2Fchrome_48x48.png" alt="Chrome"&gt;&lt;/a&gt;&lt;br&gt;Chrome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/vimmatic/" rel="noopener noreferrer"&gt;Get an add-on&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://chrome.google.com/webstore/detail/vimmatic/pghmfgnakhjiphmlcnhfpgopkcjhiedc" rel="noopener noreferrer"&gt;Get an extension (experimental support)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that the Firefox requires an permission to run add-on on certain page.  To run the add-on on any pages, ensure that "Access your data for all websites" is enabled on permissions tab on add-on settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;Vimmatic provides Vim-like key binds.  To scroll a page in the browser, press k, j, h and l keys.  You can scroll to the top or the bottom of a page by gg and G.&lt;/p&gt;

&lt;p&gt;To select a left and right of current tab, use K and J respectively.  To close current tab, use d and to restore closed tabs, use u.&lt;/p&gt;

&lt;p&gt;See also &lt;a href="https://ueokande.github.io/vimmatic/keymaps.html" rel="noopener noreferrer"&gt;Keymaps on official document&lt;/a&gt; for more detailed of keymaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow mode
&lt;/h2&gt;

&lt;p&gt;To open a link, press f to enter the &lt;strong&gt;follow mode&lt;/strong&gt; to select a link.  Then you can select links by alphabetic keys.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Using command line
&lt;/h2&gt;

&lt;p&gt;Vimmatic supports command line to run commands that control tabs and opens a tab.  To open command line, press :.&lt;/p&gt;

&lt;p&gt;To open a tab with URL, use &lt;code&gt;open&lt;/code&gt; command as the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:open https://github.com/ueokande/vimmatic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or search keywords with search engine (such as Google) like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:open How to use Vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see completed commands on &lt;a href="https://ueokande.github.io/vimmatic/console_commands.html" rel="noopener noreferrer"&gt;Console commands&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>vim</category>
      <category>browser</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Install your chrome extensions and firefox add-ons to the browser with playwright</title>
      <dc:creator>Shin'ya Ueoka</dc:creator>
      <pubDate>Sun, 21 Aug 2022 13:36:44 +0000</pubDate>
      <link>https://dev.to/ueokande/install-your-chrome-extensions-or-firefox-add-ons-to-the-browser-with-playwright-1lpl</link>
      <guid>https://dev.to/ueokande/install-your-chrome-extensions-or-firefox-add-ons-to-the-browser-with-playwright-1lpl</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqwe6574sp5hm3bhpev5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqwe6574sp5hm3bhpev5.gif" alt="Screenshot of playwright-webextext"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; is one of the most remarkable frameworks to achieve browser automation.  It officially supports running Chromium, Firefox, and WebKit.  How can we install chrome extensions or firefox add-ons to the browser on playwright?  Playground introduces a practice for chromium on the &lt;a href="https://playwright.dev/docs/chrome-extensions" rel="noopener noreferrer"&gt;document&lt;/a&gt;, but firefox is not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ueokande/playwright-webextext" rel="noopener noreferrer"&gt;Playwright-webextext&lt;/a&gt; stands to enable the installation of chrome extensions and firefox add-ons to the browser.  It extends playground APIs and test fixtures that allow you to use chrome extensions or firefox add-ons from your local file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Playwright-webextext is distributed on &lt;a href="https://www.npmjs.com/package/playwright-webextext" rel="noopener noreferrer"&gt;npm&lt;/a&gt;.  You can install it by &lt;code&gt;npm&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; playwright-webextext
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Or install with &lt;code&gt;yarn&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yarn add playwright-webextext
&lt;span class="go"&gt;

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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;
&lt;h3&gt;
  
  
  withExtension()
&lt;/h3&gt;

&lt;p&gt;This method extends playwright's core APIs.  The method returns a custom BrowserType to load temporary extensions or add-ons from the local filesystem.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;firefox&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withExtension&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright-webextext&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="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;browserTypeWithExtension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// base browser type&lt;/span&gt;
    &lt;span class="nx"&gt;firefox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// path to a directory containing manifest.json&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path/to/your/extensions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// launch a browser&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;browserTypeWithExtension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that using chrome extensions has two limitations: 1) the browser should run on headed mode, and 2) you should launch the browser with a persistent context.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;browserTypeWithExtension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launchPersistentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  createFixture()&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;This method provides custom fixtures to run tests with a custom BrowserType.  The method prepares a browser to install chrome extensions or firefox add-ons before tests start.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createFixture&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playwright-webextext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createFixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path/to/your/extensions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should launch browser with extensions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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;br&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&lt;a href="https://example.com/" rel="noopener noreferrer"&gt;https://example.com/&lt;/a&gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  How does it work&lt;br&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Chromium
&lt;/h3&gt;

&lt;p&gt;Chromium supports installing extensions via &lt;a href="https://sites.google.com/site/chromeappupdates/launch-switches" rel="noopener noreferrer"&gt;command-line options&lt;/a&gt;.  Playwright-webextext launches a chromium browser with command-line options with extension paths.  See the following document for more details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/docs/chrome-extensions" rel="noopener noreferrer"&gt;Chrome Extensions | Playwright&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;p&gt;Firefox provides a remote debugging server to control the browser via a &lt;a href="https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html" rel="noopener noreferrer"&gt;remote debugging protocol&lt;/a&gt;.  This protocol also enables installing a temporary add-on.  Playwright-webextext installs add-ons by this protocol.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>playwright</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a monolithic application with Go and React</title>
      <dc:creator>Shin'ya Ueoka</dc:creator>
      <pubDate>Sun, 14 Nov 2021 06:11:45 +0000</pubDate>
      <link>https://dev.to/ueokande/building-a-monolithic-application-with-go-and-react-2i63</link>
      <guid>https://dev.to/ueokande/building-a-monolithic-application-with-go-and-react-2i63</guid>
      <description>&lt;p&gt;The ways and practices of building web services are more and more diversified today due to the business requirements, reliability, and scalability.  The microservices or micro-frontend are well-known practices to divide a huge service and huge organization into self-organized teams so that they can maintain their services themself.  That not only achieve to increase your development cycle but also makes the service more sustainable.  Although these technics have a position with the future on scaling the service, complex architecture brings on complex development and deployment.  Sometimes they are overmuch for the non-critical services for your business like the internal services in the company.&lt;/p&gt;

&lt;p&gt;This article lets you build a monolith service using Go and React and introduces the boilerplate I published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolithic Go and React Application Boilerplate
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ueokande" rel="noopener noreferrer"&gt;
        ueokande
      &lt;/a&gt; / &lt;a href="https://github.com/ueokande/go-react-boilerplate" rel="noopener noreferrer"&gt;
        go-react-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Boilerplate for building a monolighic Go and React application
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Monolithic Go and React Application Boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ueokande/go-react-boilerplate./screenshot.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fueokande%2Fgo-react-boilerplate.%2Fscreenshot.png" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This repository provides a simple and monolithic service with a server written in Go and frontend with React.  The service does not orient the modern services (like microservices or micro frontends), but you can see the simplicity and easiness of development and deployment.  Sometimes monolithic services are helpful for a small-scale or non-critical business situation such as an internal system.&lt;/p&gt;
&lt;p&gt;This project introduce the following languages and frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server-side
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org" rel="nofollow noopener noreferrer"&gt;Golang&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;gorilla/mux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Frontend
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/" rel="nofollow noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/" rel="nofollow noopener noreferrer"&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactrouter.com/" rel="nofollow noopener noreferrer"&gt;react-router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://axios-http.com/" rel="nofollow noopener noreferrer"&gt;axios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mui.com/" rel="nofollow noopener noreferrer"&gt;MUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The application serves a tiny social blog as a sample.  The users can publish the articles and add comments.  Feel free to customize and build your applications based on this project.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quickstart (for development)&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The repository contains two projects; for the frontend and server-side.  The frontend project is in &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/frontend" rel="noopener noreferrer"&gt;&lt;code&gt;frontend&lt;/code&gt;&lt;/a&gt; sub-project,  you can install dependencies and run the debug server by &lt;code&gt;yarn&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ &lt;span class="pl-s1"&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; frontend&lt;/span&gt;
$ &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ueokande/go-react-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The project introduce the following languages and frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-side

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://golang.org" rel="noopener noreferrer"&gt;Golang&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;gorilla/mux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Frontend

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactrouter.com/" rel="noopener noreferrer"&gt;react-router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://axios-http.com/" rel="noopener noreferrer"&gt;axios&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mui.com/" rel="noopener noreferrer"&gt;MUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The application serves a tiny social blog as a sample.  The users can publish the articles and add comments.  Feel free to customize and build your applications based on this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart (for development)
&lt;/h2&gt;

&lt;p&gt;The repository contains two projects; for the frontend and server-side.  The frontend project is in &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/frontend" rel="noopener noreferrer"&gt;&lt;code&gt;frontend&lt;/code&gt;&lt;/a&gt; sub-project,  you can install dependencies and run the debug server by &lt;code&gt;yarn&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn start
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Then run server-side service in debug mode by &lt;code&gt;go run&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go run main.go  &lt;span class="nt"&gt;-debug&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;You can see the sample application on &lt;code&gt;http://localhost:8000&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application: A minimal social blog
&lt;/h2&gt;

&lt;p&gt;The application provides a minimal social blog as a sample.  You can retrieve the articles and publish your article via your browser.  You are also able to send your comment to the articles.&lt;/p&gt;

&lt;p&gt;Any frontend page consists of a single page known as a single-page application;  the server responds with a single HTML. The pages transit seamlessly.  The frontend scripts fetch and send articles and comments via APIs asynchronously using  &lt;a href="https://axios-http.com/" rel="noopener noreferrer"&gt;axios&lt;/a&gt;.  The server-side application provides the following REST APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/health&lt;/code&gt; and &lt;code&gt;GET /api/ready&lt;/code&gt;: The heath check endpoints to ensure the application lives.  You can see more detail in &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/" rel="noopener noreferrer"&gt;Kubernetes docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/articles&lt;/code&gt;: Get all articles with summaries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/articles/{article_id}&lt;/code&gt;: Get a article with full content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/articles&lt;/code&gt;: Create a new article.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/articles/{article_id}/comments&lt;/code&gt;: Get comments of the article &lt;code&gt;article_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/articles/{article_id}/comments&lt;/code&gt;: Create a new comment for the article_id &lt;code&gt;article_id&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The server-side go service has a debug mode to serve the webpack development server on the same endpoint with APIs.  This is useful to make the frontend scripts able to access APIs with no CORS headers.  Any requests excluding the path starting with &lt;code&gt;/api/&lt;/code&gt; returns assets served from webpack&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-side
&lt;/h3&gt;

&lt;p&gt;The server-side program has two packages, &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/repository" rel="noopener noreferrer"&gt;&lt;code&gt;repository&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/web" rel="noopener noreferrer"&gt;&lt;code&gt;web&lt;/code&gt;&lt;/a&gt;.  The &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/repository" rel="noopener noreferrer"&gt;&lt;code&gt;repository&lt;/code&gt;&lt;/a&gt; packages contain interfaces and implementation to read and persist user's requests.  Note that the current implementation server never keeps data. The server loses the article you published or the comments you sent when the restart. If you wish to persist them, try to implement repositories instead of mock storing to the in-memory.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/web" rel="noopener noreferrer"&gt;&lt;code&gt;web&lt;/code&gt;&lt;/a&gt; packages provide the routes of the request from user's URLs and present responses for the request.  They are known as the controller layer in MVC or DDD.  If you want to add some APIs or add models, implement them to &lt;code&gt;repository&lt;/code&gt; and &lt;code&gt;web&lt;/code&gt;, respectively.  If you need more complex logic or use cases, feel free to implement or add new packages.&lt;/p&gt;

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

&lt;p&gt;Any page consists of a single page as known as a single-page application.  The &lt;a href="https://reactrouter.com/" rel="noopener noreferrer"&gt;react-router&lt;/a&gt; is a library to achieve client-side routing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;frontend&lt;/code&gt; sub-project is initialized by &lt;a href="https://www.npmjs.com/package/react-scripts" rel="noopener noreferrer"&gt;react-scripts&lt;/a&gt; and keeps the directory structure and build processes.  Every component is in &lt;code&gt;frontend/src&lt;/code&gt; in flatten.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to deploy it to the production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual deploy
&lt;/h3&gt;

&lt;p&gt;You should not use a webpack development server by &lt;code&gt;yarn start&lt;/code&gt; and &lt;code&gt;go run&lt;/code&gt; in production.  You need to build them before deployment.  To generate frontend assets, run &lt;code&gt;yarn build&lt;/code&gt;.  This command generates minified HTML, JavaScript, and CSS.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn build&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;To compile a server-side application to create an executable binary, use &lt;code&gt;go build&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; go-react-boilerplate &lt;span class="nt"&gt;-trimpath&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Then you can confirm to run it by the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

./go-react-boilerplate -webroot ./frontend/build


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

&lt;/div&gt;

&lt;p&gt;The server-side also serves asset files from the directory specified by &lt;code&gt;-webroot&lt;/code&gt;, so you do not need some 3rd-party HTTP server like Apache HTTP server or NGINX.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Docker container
&lt;/h3&gt;

&lt;p&gt;The repository contains &lt;a href="https://github.com/ueokande/go-react-boilerplate/tree/master/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt;.  Using docker containers allows you to develop applications faster and make deployment easier.  The Dockerfile uses multi-stage builds; it contains steps to build frontend and server-side phases.  You can build a docker image and run the container from the image by &lt;code&gt;docker build&lt;/code&gt; and &lt;code&gt;docker run&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; go-react-builderplate &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 go-react-builderplate
&lt;span class="go"&gt;

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

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>react</category>
    </item>
    <item>
      <title>How to evict specific pods on the Kubernetes cluster</title>
      <dc:creator>Shin'ya Ueoka</dc:creator>
      <pubDate>Sun, 24 Oct 2021 08:31:28 +0000</pubDate>
      <link>https://dev.to/ueokande/how-to-evict-specific-pods-on-the-kubernetes-cluster-1p44</link>
      <guid>https://dev.to/ueokande/how-to-evict-specific-pods-on-the-kubernetes-cluster-1p44</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Kubernetes is a very popular and the most widely used container orchestration.  It has a sophisticated and flexible scheduling system to deploy your application on production.  The administrators would use a kubectl CLI to manage the cluster and deployed pods.  The kubectl CLI provides enough features to manage the cluster, but the developer can customize CLI with a kubectl plugin.  This article will present what pod eviction is and introduce a kubectl plugin to do that, &lt;a href="https://github.com/ueokande/kubectl-evict" rel="noopener noreferrer"&gt;kubectl-evict&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pod Eviction
&lt;/h2&gt;

&lt;p&gt;Kubernetes provides &lt;em&gt;pod eviction&lt;/em&gt;, which stops pods and removes them from the node.  The pod eviction is usually used when the node is shutting down, or the cluster performs a rolling restart of nodes to minimize the downtime of the service.  The kubectl CLI has a sub-command &lt;code&gt;kubectl drain&lt;/code&gt; to evict pods from the specified node and, the command makes the node as unschedulable:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl drain your-node
&lt;span class="go"&gt;

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

&lt;/div&gt;
&lt;h2&gt;
  
  
  kubectl-evict: evicts specific pods
&lt;/h2&gt;

&lt;p&gt;The kubectl CLI does not provide a feature to evict specific pods. I sometimes need this feature to safely remove pods from the node or test a &lt;a href="https://kubernetes.io/docs/tasks/run-application/configure-pdb/" rel="noopener noreferrer"&gt;PodDisruptionBudget&lt;/a&gt;.  The &lt;a href="https://github.com/ueokande/kubectl-evict" rel="noopener noreferrer"&gt;kubectl-evict&lt;/a&gt; is a kubectl plugin that allows evicting certain pods from the node.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ueokande" rel="noopener noreferrer"&gt;
        ueokande
      &lt;/a&gt; / &lt;a href="https://github.com/ueokande/kubectl-evict" rel="noopener noreferrer"&gt;
        kubectl-evict
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A kubectl plugin to evict pods
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Although there are already plugins to evict pods such as &lt;a href="https://github.com/dwradcliffe/kubectl-evict" rel="noopener noreferrer"&gt;dwradcliffe/kubectl-evict&lt;/a&gt; and &lt;a href="https://github.com/rajatjindal/kubectl-evict-pod" rel="noopener noreferrer"&gt;kubectl-evict-pod&lt;/a&gt;, the above plugin has a high compatibility interface with &lt;code&gt;kubectl logs&lt;/code&gt; or &lt;code&gt;kubectl exec&lt;/code&gt; and provides the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows evicting pods by a label selector&lt;/li&gt;
&lt;li&gt;Evicts pods created by Deployment or DaemonSet&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;--dry-run&lt;/code&gt; and &lt;code&gt;--grace-period&lt;/code&gt; flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can get the plugin by the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/ueokande/kubectl-evict@latest
&lt;span class="go"&gt;

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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;To evict a specific pod:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl evict nginx-abcd-1234
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;It also allows evicting pods by label selector.  The following shows to evict pods which has a label &lt;code&gt;app=nginx&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl evict &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Evict pods created by Deployment:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl evict deployment/nginx
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;You can of course evict pods from the node:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl evict node/worker-1
&lt;span class="go"&gt;

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

&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;kubectl evict&lt;/code&gt; does NOT make the node as unschedulable (cordon), so use &lt;code&gt;kubectl cordon&lt;/code&gt; or &lt;code&gt;kubectl drain&lt;/code&gt; if you need to do this.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>go</category>
    </item>
  </channel>
</rss>
