<?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: Genne23v</title>
    <description>The latest articles on DEV Community by Genne23v (@genne23v).</description>
    <link>https://dev.to/genne23v</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%2F636025%2F953ef04b-d12a-4e6f-a19e-f8d056fa830e.jpeg</url>
      <title>DEV Community: Genne23v</title>
      <link>https://dev.to/genne23v</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/genne23v"/>
    <language>en</language>
    <item>
      <title>Writing Playwright UI Test</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Mon, 17 Apr 2023 23:32:11 +0000</pubDate>
      <link>https://dev.to/genne23v/writing-playwright-ui-test-4g93</link>
      <guid>https://dev.to/genne23v/writing-playwright-ui-test-4g93</guid>
      <description>&lt;p&gt;As Starchart is getting close to the launch, we have more UI changes than before. So I needed to learn how to write Playwright UI test for my recent PRs. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nKfyAuUC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xl3q2ib5rdz0in6a1hw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nKfyAuUC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xl3q2ib5rdz0in6a1hw.png" alt="playwright test report" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Playwright is a versatile UI testing frameworks which works on any browser and any platform. I used Selenium and Cypress before. In my opinion, Playwright's multi-browser support is easy to set up and the the way it creates a report that is easy to understand and debug. I really like that Playwright retries failed tests and sets a flaky flag even if it passes in another try. And you can run tests in parallel that reduces the deployment time on CI pipeline. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pLIxuD1K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn2vjyft9uiuiml1minl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pLIxuD1K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mn2vjyft9uiuiml1minl.png" alt="failed test debugging" width="800" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to set up Playwright
&lt;/h3&gt;

&lt;p&gt;You just need to run this command to install Playwright in your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init playwright@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you will have &lt;code&gt;playwright.config.ts&lt;/code&gt; file and &lt;code&gt;tests&lt;/code&gt; folder in your project root folder. &lt;/p&gt;

&lt;p&gt;To run the test,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the test reports,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright show-report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing Playwright test
&lt;/h3&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;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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&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;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Certificate Page&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="o"&gt;=&amp;gt;&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;beforeEach&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;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="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/certificate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;//Pre-condition for each test&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request a Certificate&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&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;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;Certificate&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;domainName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user1.starchart.com&lt;/span&gt;&lt;span class="dl"&gt;'&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user1.starchart.com&lt;/span&gt;&lt;span class="dl"&gt;'&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titleHeader&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Certificate&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;You can set up a suite of tests by declaring &lt;code&gt;test.describe('...')&lt;/code&gt;. Everything inside this function is a subset of this test suite. &lt;/p&gt;

&lt;p&gt;And often you have to set up pre-condition or clear what certain test has changed. In this case, you can use &lt;code&gt;test.beforeAll&lt;/code&gt;, &lt;code&gt;test.beforeEach&lt;/code&gt;, &lt;code&gt;test.afterAll&lt;/code&gt;, &lt;code&gt;test.beforeEach&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Then the actual test goes with the tile of the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request a Certificate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test UI, you need to select UI element for action or assertion. There are multiple ways of selecting a certain UI element. When it is looking for fixed content in the UI component, &lt;a href="https://playwright.dev/docs/api/class-framelocator"&gt;Frame Locator&lt;/a&gt; is easy to write and read tests. And you can use &lt;a href="https://playwright.dev/docs/locators#filtering-locators"&gt;filter locators&lt;/a&gt; when you have multiple elements from Frame Locator. &lt;br&gt;
When the content in a UI element is dynamic, you can also use &lt;a href="https://playwright.dev/docs/other-locators"&gt;CSS locator&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Codegen Test Generator
&lt;/h3&gt;

&lt;p&gt;You can simply let Playwright generate tests by using &lt;code&gt;Codegen&lt;/code&gt;. Run this command in your terminal. You don't have to add an URL here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright codegen &lt;span class="o"&gt;[&lt;/span&gt;URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will open up a browser. Every action you make to the browser will generate test code on the other window. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ObMbR7xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gdx0vksw0bsrr93zt8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ObMbR7xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gdx0vksw0bsrr93zt8u.png" alt="codegen screen" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use Codegen in VS Code. &lt;a href="https://playwright.dev/docs/getting-started-vscode#generating-tests"&gt;Generating Tests&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Playwright Configuration
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;playwright.config.ts&lt;/code&gt; file, import &lt;code&gt;PlaywrightTestConfig&lt;/code&gt; type and &lt;code&gt;devices&lt;/code&gt;. Add config setting in the object.&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;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&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;@playwright/test&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;devices&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;@playwright/test&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;testDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./test/e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Specify Playwright test folder&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Timeout for single test&lt;/span&gt;
  &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Timeout for expect()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;fullyParallel&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="c1"&gt;// Run tests in parallel&lt;/span&gt;
  &lt;span class="na"&gt;retries&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="c1"&gt;// Retry set&lt;/span&gt;
  &lt;span class="na"&gt;workers&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="c1"&gt;// Number of workers running test&lt;/span&gt;
  &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Test report format&lt;/span&gt;
  &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;actionTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Time limit for action (0 is no limit)&lt;/span&gt;
    &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;//Base URL setup&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on-first-retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Trace condition set&lt;/span&gt;
    &lt;span class="na"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on-first-retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Recording condition set&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// Browser setting&lt;/span&gt;
  &lt;span class="na"&gt;projects&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;Google Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome&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;dependencies&lt;/span&gt;&lt;span class="p"&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;setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;testIgnore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;mobile&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;spec&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;ts/&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;Mobile Safari&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deviceScaleFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;deviceScaleFactor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isMobile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hasTouch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;hasTouch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;defaultBrowserType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iPhone 12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;defaultBrowserType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="p"&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;setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;testIgnore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;desktop&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;spec&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;ts/&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;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There are many other features to introduce. You can emulate browser setting such as locale, timezone, viewport, etc., as well as mock API, set a fixture. Official documentation is always the best place to start with and you can learn more details about features and ask questions to solve your problems. And there are always multiple framework choices for a certain purpose. I think it's good to try a different framework in your project to compare features and learn more technology from it. I think Playwright is a good choice for your UI testing. &lt;/p&gt;

</description>
      <category>starchart</category>
      <category>senecaosd700</category>
      <category>playwright</category>
    </item>
    <item>
      <title>What is HTTPS?</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 08 Apr 2023 17:59:42 +0000</pubDate>
      <link>https://dev.to/genne23v/what-is-https-32i7</link>
      <guid>https://dev.to/genne23v/what-is-https-32i7</guid>
      <description>&lt;p&gt;HTTPS stands for Hypertext Transfer Protocol Secure which is the encrypted version of HTTP. It is a protocol used for secure communication over the internet. When you visit an HTTPS website, data is encrypted to exchange between your web browser and the website. So your data is confidential from someone else and the data integrity can be ensured. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tpX4j2Bp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rojg1faae5furyj5sp33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tpX4j2Bp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rojg1faae5furyj5sp33.png" alt="https secured website" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How does HTTPS work?
&lt;/h3&gt;

&lt;p&gt;HTTPS needs a certificate from third-party to secure communication and verify that the website is compliant. This certificate is known as SSL (Secure Socket Layer) certificate. This certificate encrypts a connection and provides another layer of security for sensitive data that &lt;br&gt;
 must not be accessed by any attackers. This additional security can be extremely important to protect secret information such as user's identification information, address, credit card number, etc. &lt;/p&gt;

&lt;h3&gt;
  
  
  How is data secured in HTTPS?
&lt;/h3&gt;

&lt;p&gt;HTTPS encryption uses TLS (Transport Layer Security) protocol. It requires private key and public key to encrypt data between two parties. The private key is controlled by the owner of a website. It's used to decrypt the data that is encrypted by a public key. The public key is available to everyone who interacts with the website securely. &lt;/p&gt;

&lt;h3&gt;
  
  
  How does SSL certificate look like?
&lt;/h3&gt;

&lt;p&gt;Below picture is one example of SSL certificate. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GPblBN8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kv0ww78nh6bioiqi0bsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GPblBN8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kv0ww78nh6bioiqi0bsb.png" alt="ssl certificate" width="588" height="497"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;b&gt;Image Source: 
&lt;/b&gt;https://www.geocerts.com/support/generating-a-csr-for-a-wildcard-ssl-certificate



&lt;p&gt;It contains following information. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The domain name that the certificate was issued for&lt;/li&gt;
&lt;li&gt;Where the certificate was issued to&lt;/li&gt;
&lt;li&gt;Which certificate authority issued it&lt;/li&gt;
&lt;li&gt;Issue/Expiration date of the certificate&lt;/li&gt;
&lt;li&gt;The certificate authority's digital signature&lt;/li&gt;
&lt;li&gt;Associated subdomains&lt;/li&gt;
&lt;li&gt;The public key (the private key must not be publicly available)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the certificate is issued, installation and activation are necessary on the website's origin server. When it's activated on the server, the website will load over HTTPS and all incoming/outcoming traffic from the website will be encrypted and secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who can provide SSL certificate?
&lt;/h3&gt;

&lt;p&gt;Domains need to obtain valid SSL certificate from a certificate authority (CA). A CA is a trusted third SSL certificate provider. The CA will also digitally sign the certificate with their own private key, allowing clients to verify it. Most CAs will charge a fee for issuing an SSL certificate. What Starchart is using is Let's Encrypt which provides SSL certificates for free as a non-profit SSL certificate provider. Both free and paid SSL certificate providers offers all necessary security. You need to choose a provider based on your need. You can read more in this article. &lt;a href="https://www.icdsoft.com/blog/a-free-or-a-paid-ssl-certificate-which-one-is-better/#:~:text=If%20you%20only%20want%20to,in%20the%20browser%20address%20bar"&gt;A free or a paid SSL certificate - which one is better&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;To build a website that exchanges critical information such as transaction data, important personal information, etc.,  HTTPS is mandatory. Although I didn't write any code for certificate related part, Starchart gave me an opportunity to learn more about SSL certificate. I think this is one good aspect about contributing to open source project since I usually build a project on subjects that I'm most familiar with. I'm learning many tech stacks and more detailed knowledges about internet through Starchart. &lt;/p&gt;

</description>
      <category>starchart</category>
      <category>senecaosd700</category>
    </item>
    <item>
      <title>What is DNS Record?</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 01 Apr 2023 19:01:54 +0000</pubDate>
      <link>https://dev.to/genne23v/what-is-dns-record-1cbk</link>
      <guid>https://dev.to/genne23v/what-is-dns-record-1cbk</guid>
      <description>&lt;p&gt;It's been about 10 weeks since I started working Starchar project which is the DNS service for student projects. At the beginning, many of us didn't have clear understanding about the terms like domain, subdomain, name, records, etc. Now I have a better understanding of these terms now. I would like to share the terms that I have learned in this project. &lt;/p&gt;

&lt;h3&gt;
  
  
  What is DNS Record?
&lt;/h3&gt;

&lt;p&gt;A DNS record is a type of data stored in a domain name system (DNS) server that maps a domain name to an IP address or other information about the domain. So what Starchart will do is to create a DNS record that is connected to the user's IP address or domain. Also it can be linked with text type information. I will explain more details about these types.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sc7AFYSf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m88r5c6fj1qzjxm0cw3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sc7AFYSf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m88r5c6fj1qzjxm0cw3u.png" alt="starchart dns record creation" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of DNS Record
&lt;/h3&gt;

&lt;p&gt;As I mentioned previously there are types of record that can be linked with domain name. Starchart supports A, AAAA, CNAME, TXT types. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Record: Maps a domain name to an IPv4 address. So you can name your subdomain like &lt;code&gt;my-project&lt;/code&gt;  and enter your public IP address as an A record. Your local project can be accessed via &lt;code&gt;my-project.user.mystudentproject.ca&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;AAAA Record: Maps a domain name to an IPv6 address. It works same as above. You just need to select AAAA type and enter IPv6 format IP address.&lt;/li&gt;
&lt;li&gt;CNAME (Canonical Name) Record: Maps a domain name to another domain name. If the user has an own domain already, it can be mapped with other domain name. So your project have two different domain names. Also CNAME is often used for subdomains. &lt;/li&gt;
&lt;li&gt;TXT (Text) Record: Allows the domain owner to add arbitrary text to the DNS record. There are many different types of TXT records such as SPF, DKIM, DMARC, etc. This is one example of SPF record.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"v=spf1 ip4:192.168.0.1/16 -all"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specific TXT record means that use SPF version 1 and email should be sent from a range between &lt;code&gt;192.168.0.1&lt;/code&gt; and &lt;code&gt;192.168.255.255&lt;/code&gt; and deny all sources. You can find more details of other TXT record types in this &lt;a href="https://support.google.com/a/answer/2716800?hl=en#:~:text=TXT%20records%20are%20a%20type,outgoing%20email%20from%20your%20domain"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are also other types of record as below. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MX (Mail Exchanger) Record: Specifies the mail server responsible for handling email for a domain.&lt;/li&gt;
&lt;li&gt;NS (Name Server) Record: Specifies the authoritative DNS server for a domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What will Starchart do?
&lt;/h3&gt;

&lt;p&gt;Students can create their own subdomain under the root domain &lt;code&gt;mystudentproject.ca&lt;/code&gt; using Starchart. Starchart will create a DNS record using student's subdomain name as well as HTTPS certificate. Then the project will be publicly accessed and secured on the internet. &lt;br&gt;
Route53 allows up to 10,000 records in one hosted zone which means same as 10,000 subdomains under &lt;code&gt;mystudentproject.ca&lt;/code&gt;. Another hosted zone will incur more cost. You can plan the budget using AWS Route53 pricing &lt;a href="https://aws.amazon.com/route53/pricing"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Terms in DNS
&lt;/h3&gt;

&lt;p&gt;Route53 DNS records consist of &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;Name&lt;/code&gt;, &lt;code&gt;Value&lt;/code&gt;, &lt;code&gt;TTL&lt;/code&gt; and other optional values. Here &lt;code&gt;Name&lt;/code&gt; means fully qualified domain name, aka &lt;code&gt;FQDN&lt;/code&gt;. And Starchart only takes a subdomain from the user to complete the input for Route53 &lt;code&gt;Name&lt;/code&gt;. &lt;code&gt;TTL&lt;/code&gt; is Time to Live that decides how long it takes for record updates to reach the users. Longer TTL speeds up DNS lookups by increasing the chance of cached results, at the same time the updates to your records take longer to go into effect.&lt;/p&gt;

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

&lt;p&gt;We didn't have the right naming for domain related variables. One example is that I completely forgot what my variable &lt;code&gt;domain&lt;/code&gt; meant even though I wrote the DNS code by myself. So we changed the variable names to either &lt;code&gt;fqdn&lt;/code&gt; or &lt;code&gt;subdomain&lt;/code&gt;. I can imagine later a new contributor would have great confusion from variable names. If I had a better knowledge, I would have used the appropriate naming. I hope this post is helpful to understand DNS record system and Starchart. &lt;/p&gt;

</description>
      <category>starchart</category>
      <category>senecaosd700</category>
      <category>dnsrecord</category>
    </item>
    <item>
      <title>Managing Work Scope in Software Project</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 25 Mar 2023 13:59:59 +0000</pubDate>
      <link>https://dev.to/genne23v/managing-work-scope-in-software-project-5eic</link>
      <guid>https://dev.to/genne23v/managing-work-scope-in-software-project-5eic</guid>
      <description>&lt;p&gt;Understanding the code base, writing good code, and flawless communication with maintainer are the fundamentals of the open source work. Luckily, I work with some of very experienced people in Starchart project. I learned that work can be faster when managing the work efficiently. Here's my lessons learned. &lt;/p&gt;

&lt;h3&gt;
  
  
  Minimize your work scope
&lt;/h3&gt;

&lt;p&gt;If the PR has thousand line changes, it takes a lot of efforts not just for the code writer, but also the code reviewer. Also it means more chances of breaking existing code base. So it will take more than a week to get merged after rounds of reviews and updates. The longer the PR is under review, the more the PR gets conflicts especially if the project is very active. You can write sub issues of a large issue to minimize the work. If some parts of the issue is not the core of the work, you can separate for later follow-ups. Any work should be planned to finish within a week as project or organization usually is run on a weekly basis. &lt;/p&gt;

&lt;h3&gt;
  
  
  Split large scope of work into different phases
&lt;/h3&gt;

&lt;p&gt;Planning big work into multiple phases is not easy task unless experienced. But this is something that I learned in Starchart project. The experienced code writer plans multiple steps for difficult tasks. I have seen the experienced developers build skeleton code without, write comments to show the intention, and structure the files without functionality yet. You can still have good code review feedbacks based on the function flow. Then slowly build up each module or functions. This way the work can be processed faster as you don't have to finish a large module at once. Instead you can finish your function, get it merged, and work on next function. I know it's not easy to plan things if the area of the code is new to the code writer. But I think it's a great skill and it's better to practice now. &lt;/p&gt;

&lt;h3&gt;
  
  
  Plan future improvement instead of fixing everything in one PR
&lt;/h3&gt;

&lt;p&gt;Often times you get a feedback to change something during the code review. That change request can be essential but sometimes it can be improved later. Then you can suggest that you will write another issue to follow up. This way you can avoid another round of review and keep your change scope as originally intended. &lt;/p&gt;

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

&lt;p&gt;When I work alone, I only focus on implementing features and fixing bugs. But larger project with a team requires more than implementing code. Design the system, continuously refactor code for better maintainability, manage issues and review someone else's code. And this planning the work will make a better look of the developer. The work progress is fast and easy to review. This technique will be more obvious in real world projects if you see PRs from the experienced. &lt;/p&gt;

</description>
      <category>starchart</category>
      <category>senecaosd700</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Advanced Types in TypeScript</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sun, 19 Mar 2023 04:56:51 +0000</pubDate>
      <link>https://dev.to/genne23v/advanced-types-in-typescript-2281</link>
      <guid>https://dev.to/genne23v/advanced-types-in-typescript-2281</guid>
      <description>&lt;p&gt;Strongly typed languages increase the safety of programs. It doesn't mean that strongly typed languages are always better than weakly typed languages. Weakly typed languages can reduce the development time. So programmers should choose a programming language based on the purpose of the program. Although I contributed some of Typescript projects, but I didn't have much knowledge about Typescript typing. I learned some of very useful typing techniques during Starchart code review. &lt;/p&gt;

&lt;h3&gt;
  
  
  When using the same type with different property names
&lt;/h3&gt;

&lt;p&gt;You could face the situation that it doesn't make sense to have two different type names while only small portion of the properties varies. My DNS module in Starchart is a good example to explain this instance. At the beginning, we use the variable &lt;code&gt;name&lt;/code&gt; as any type of domains. But in our app, this name could be &lt;code&gt;subdomain&lt;/code&gt; or &lt;code&gt;fully qualified domain name&lt;/code&gt; which can be shortened to &lt;code&gt;fqdn&lt;/code&gt;. This is very confusing when writing a code to use the function. You don't have any idea whether you are going to get &lt;code&gt;subdomain&lt;/code&gt; or &lt;code&gt;fqdn&lt;/code&gt; which could result in a wrong implementation or frustrating the writer to look up all the code to figure what it actually is. So we can do something 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="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;subdomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fqdn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseDnsRequestData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DnsRequestData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BaseDnsRequestData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;DnsRequestData&lt;/code&gt; can have either &lt;code&gt;subdomain&lt;/code&gt; or &lt;code&gt;fqdn&lt;/code&gt;. When you extract the data, Typescript knows what type is passing only in runtime. So you should extract the data as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;name&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subdomain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&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;subdomain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&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;fqdn&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;h3&gt;
  
  
  When type naming is not necessary
&lt;/h3&gt;

&lt;p&gt;Sometimes it's better not to name the type when passing data is obvious or varying similar names is more confusing. So you can instead of defining as 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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DnsRequestData&lt;/span&gt; &lt;span class="o"&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="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ports&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;You can simplify the type without naming.&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;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;It was really good for me not just to learn these techniques, but also to help me to write better code. It seems not important for someone who develops something quickly, but typing is very important when many contributors or developers are working together. It finds the potential errors quickly and gives an idea of what this code does. It also takes a lot of learning from the experienced. I never thought about this kind of problems when I was working alone. Having the experienced developers' feedback is really helpful to learn how to write better code. Thanks for reading my article! &lt;/p&gt;

</description>
      <category>typescript</category>
      <category>starchart</category>
      <category>senecaosd700</category>
    </item>
    <item>
      <title>JavaScript &amp; TypeScript ORM Prisma</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 11 Mar 2023 01:40:49 +0000</pubDate>
      <link>https://dev.to/genne23v/javascript-typescript-orm-prisma-5hg4</link>
      <guid>https://dev.to/genne23v/javascript-typescript-orm-prisma-5hg4</guid>
      <description>&lt;p&gt;Our open-source project &lt;code&gt;Starchart&lt;/code&gt; uses &lt;code&gt;Prisma&lt;/code&gt; which is ORM for any JavaScript and TypeScript projects. As it states on the doc, it's the next generation ORM that provides a lot of convenient features. I'm really amazed how it makes things easier our project. Let me show how to use Prisma briefly in this blog. &lt;/p&gt;

&lt;h3&gt;
  
  
  Getting started
&lt;/h3&gt;

&lt;p&gt;Once you have your project ready, you can install Prisma with below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;prisma &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And initiate Prisma in your project. You can choose your data source like SQLite, PostgreSQL, MySQL, MariaDB, MongoDB, MS SQL server, etc. In this case, I choose MySQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma init &lt;span class="nt"&gt;--datasource-provider&lt;/span&gt; mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing schema and init PrismaClient
&lt;/h3&gt;

&lt;p&gt;Now it's ready to use Prisma. Let's set up some schemas.&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;model&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;       &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;  &lt;span class="nx"&gt;DateTime&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;updatedAt&lt;/span&gt;  &lt;span class="nx"&gt;DateTime&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;updatedAt&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
  &lt;span class="kr"&gt;public&lt;/span&gt;     &lt;span class="nb"&gt;Boolean&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;record&lt;/span&gt;     &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;           &lt;span class="nx"&gt;Int&lt;/span&gt;       &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
  &lt;span class="nx"&gt;username&lt;/span&gt;     &lt;span class="nb"&gt;String&lt;/span&gt;   
  &lt;span class="nx"&gt;description&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt;    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VarChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;         &lt;span class="nx"&gt;User&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example to show how to write Prisma schema. You can see how to set ID column, make a property unique in the table, set initial date property when created, as well as default value and length limitation. And the table relationship between &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Record&lt;/code&gt;. You can find more details on Prisma docs &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#defining-attributes"&gt;Defining attributes&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CRUD operation
&lt;/h3&gt;

&lt;p&gt;Next step is to create a new SQL migration file and run the SQL migration file against database. Below command creates &lt;code&gt;dev.db&lt;/code&gt; inside the &lt;code&gt;prisma&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you need the &lt;code&gt;PrismaClient&lt;/code&gt; initialization script.&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;PrismaClient&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;@prisma/client&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;init&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;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&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;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PrismClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;datasources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$connect&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;You can write queries. Here's CRUD examples.&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;user&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john@starchart.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;records&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;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Updated record&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;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You can also use methods like &lt;code&gt;findUnique&lt;/code&gt;, &lt;code&gt;findFirst&lt;/code&gt;, &lt;code&gt;updateMany&lt;/code&gt;, &lt;code&gt;upsert&lt;/code&gt;, &lt;code&gt;deleteMany&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, etc. If you have a bit of SQL experiences, you would understand what each of these command does. Let me leave a link to take a look at further details &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-client/crud"&gt;Prisma CRUD operations&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeding and Prisma Studio
&lt;/h2&gt;

&lt;p&gt;What's great about Prisma is the various tools that makes your development easier. You can set up a seed script to create initial development data. In order to do so, you need to add a prisma script as 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="c1"&gt;//In package.json for Javascript project&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;starchart&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="s2"&gt;version&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="s2"&gt;0.0.1&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="s2"&gt;prisma&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;seed&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="s2"&gt;node prisma/seed.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;For Typescript, you need to install &lt;code&gt;@types/node&lt;/code&gt; and add Typescript version script in &lt;code&gt;package.jon&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;starchart&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="s2"&gt;version&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="s2"&gt;0.0.1&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="s2"&gt;prisma&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;seed&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="s2"&gt;ts-node prisma/seed.ts&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="s2"&gt;devDependencies&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@types/node&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="s2"&gt;^14.14.21&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;Then you can run &lt;code&gt;npx prisma db seed&lt;/code&gt; to seed your database. &lt;/p&gt;

&lt;p&gt;Prisma also provides the visual editor for easier data view/edit. &lt;/p&gt;

&lt;p&gt;You can run &lt;code&gt;npx prisma studio&lt;/code&gt; to see the visualized tables. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a9pN6IpP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o34hfce5txtoq1adylpe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a9pN6IpP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o34hfce5txtoq1adylpe.png" alt="prisma studio" width="880" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Developers need to work with database not just to write CRUD functions against database, but also database connection based on environment, create/change schema, migrate database, seed development database. Prisma can help you do these jobs easy in a sophisticated way. The fastest way to get familiar with Prisma is to go through the tutorial on the &lt;a href="https://www.prisma.io/docs/getting-started/quickstart"&gt;official website&lt;/a&gt;. I hope my article is helpful to know Prisma. &lt;/p&gt;

</description>
      <category>prisma</category>
      <category>starchart</category>
      <category>senecaosd700</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Create BullMQ Workflow</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 25 Feb 2023 01:57:05 +0000</pubDate>
      <link>https://dev.to/genne23v/create-a-bullmq-workflow-1e3o</link>
      <guid>https://dev.to/genne23v/create-a-bullmq-workflow-1e3o</guid>
      <description>&lt;p&gt;I think the one of the most challenging work for &lt;code&gt;Starchart&lt;/code&gt; app is that we need to support long asynchronous job to complete. The app adds a record to DB and create a record in Route53. Then it needs to get a HTTPS certificate. It takes at least a few minutes to complete this process. There were multiple technical approaches to solve the problem. We chose to use &lt;code&gt;BullMQ&lt;/code&gt; queue system. I hadn't tried any of queue systems before. It was a good opportunity to learn how to use &lt;code&gt;BullMQ&lt;/code&gt;, especially &lt;code&gt;Flows&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;BullMQ&lt;/code&gt; setup
&lt;/h3&gt;

&lt;p&gt;Before using &lt;code&gt;BullMQ&lt;/code&gt;, a couple of dependencies are required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i bullmq ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also you will need a &lt;code&gt;redis&lt;/code&gt; docker container to test in your local. Here's the &lt;a href="https://hub.docker.com/_/redis" rel="noopener noreferrer"&gt;Redis Docker image link&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  BullMQ Flows
&lt;/h3&gt;

&lt;p&gt;I used &lt;code&gt;BullMQ Flows&lt;/code&gt; as it suits our needs that the jobs should be run sequentially. What needs to happens is...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a record to MySQL database&lt;/li&gt;
&lt;li&gt;Create a record in Route53&lt;/li&gt;
&lt;li&gt;Continuously check Route53 whether it's deployed successfully or fails. It takes about 1 minutes according to AWS&lt;/li&gt;
&lt;li&gt;Update the Route53 sync status in DB&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write a flow for these jobs.&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;flowProducer&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;FlowProducer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addDnsRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&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="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;JobRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Step 1. Create a record in MySQL for a domain with pending status&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;addDbRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlowJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`addDbRecord:&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="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;//Set up unique name for each job&lt;/span&gt;
    &lt;span class="na"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-db-record&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;//Set a queue name. It will be used as a part of job name unless you configure it manually&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&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="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;JobRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;failParentOnFailure&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="c1"&gt;//If any one of the job fails, the whole flow will fail&lt;/span&gt;
      &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;//Retry 5 times&lt;/span&gt;
      &lt;span class="na"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;      &lt;span class="c1"&gt;//Wait for 15 seconds when retrying. Each retry wait will be exponentially increased.&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;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="nx"&gt;_000&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="c1"&gt;// Step 2. Request Route53 to create a record&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;createDnsRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlowJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`createDnsRecord:&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="nx"&gt;username&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="na"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dns&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&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="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;JobRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;failParentOnFailure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attempts&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="na"&gt;backoff&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;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="nx"&gt;_000&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="c1"&gt;// Step 3. Poll Route53 to check connection status of the domain until it's ready&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;checkDnsStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlowJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`checkDnsStatus:&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="nx"&gt;username&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="na"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkDnsStatusQueueName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;createDnsRecord&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;    &lt;span class="c1"&gt;//With this dependency, parent job will not move to queue until child job is processed&lt;/span&gt;
    &lt;span class="na"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;failParentOnFailure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attempts&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="na"&gt;backoff&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;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&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="c1"&gt;// Step 4. Update the MySQL record with the active or error status&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;syncDbStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlowJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`syncDbStatus:&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="nx"&gt;username&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="na"&gt;queueName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;syncDbStatusQueueName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;addDbRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkDnsStatus&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;    &lt;span class="c1"&gt;//This job needs two children jobs to be completed&lt;/span&gt;
    &lt;span class="na"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;failParentOnFailure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attempts&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="na"&gt;backoff&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;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flowProducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;syncDbStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;//All of jobs are added to the flow by adding the last parent process&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set up workers for each queue
&lt;/h3&gt;

&lt;p&gt;Now we need to set up a Worker to execute these jobs. Since most of the workers are similar, I will just add the most useful 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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createDnsRecordWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;JobRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;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;create-dns-record&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;//Define a queue for the worker&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;job&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&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="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&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="c1"&gt;//Get data fed from the queue. &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="nf"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not create a record in Route53&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redis&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;You can customize your worker based on your needs. When the whole flow should fail due to one of the process is failed, you need to throw &lt;code&gt;UnrecoverableError&lt;/code&gt;. &lt;br&gt;
Also you can capture values from multiple children jobs as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jobKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&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;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChildrenValues&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I haven't had an experience to use queue system. By using &lt;code&gt;BullMQ&lt;/code&gt;, we are able to solve many problems such as sending scheduled emails or notification email, creating HTTPS certificate, etc. I'm sure there are many problems that can be solved with queue system. I hope this article is useful to start writing your work flow. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>computervision</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Useful Regex For Web Developer</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Fri, 17 Feb 2023 23:51:07 +0000</pubDate>
      <link>https://dev.to/genne23v/useful-regex-for-web-developer-58i0</link>
      <guid>https://dev.to/genne23v/useful-regex-for-web-developer-58i0</guid>
      <description>&lt;p&gt;Regular expression is hard. But it's useful for web developers to limit the input from users. There are many common examples to validate user input but sometimes we need to understand it to add more logic to customize it. Let me introduce some regular expression that I often use for user input validation. &lt;/p&gt;

&lt;h3&gt;
  
  
  Limiting domain name input
&lt;/h3&gt;

&lt;p&gt;For &lt;code&gt;Starchart&lt;/code&gt; project, I needed to restrict user's subdomain name as following rules. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Domain name pattern should be [name].[studentId].rootDomain.com&lt;/li&gt;
&lt;li&gt;Domain name can contain only alphanumerical characters, along with '-' and '_'&lt;/li&gt;
&lt;li&gt;Domain name should not start or end with -&lt;/li&gt;
&lt;li&gt;Domain name cannot contain multiple consecutive '-' or '_'&lt;/li&gt;
&lt;li&gt;studentId only contains alphanumerical characters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After writing all possible pass/fail test cases, I came up with below regex. Let me explain each block of expression for better understanding.&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="o"&gt;/^&lt;/span&gt;&lt;span class="p"&gt;(?&lt;/span&gt;&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;_&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="o"&gt;!^&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;z0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^-&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;z0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;^&lt;/code&gt; means the beginning of the string. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?!.*[-_]{2,})&lt;/code&gt; -&amp;gt; &lt;code&gt;?!&lt;/code&gt; means the pattern inside the parentheses must not match for the overall regex pattern to be considered a match. &lt;code&gt;.*&lt;/code&gt; means zero or more characters and the meaning of &lt;code&gt;[-_]{2,}&lt;/code&gt; is two or more than two consecutive - &lt;code&gt;-&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt;. So multiple &lt;code&gt;-&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt; must not be in the string. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(?!^[-])&lt;/code&gt; -&amp;gt; &lt;code&gt;?!&lt;/code&gt; is the negative lookahead assertion that is explained above. &lt;code&gt;^[-]&lt;/code&gt; means the string must not start with &lt;code&gt;-&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[a-z0-9_-]+&lt;/code&gt; -&amp;gt; One or more of lowercase letters, numbers, &lt;code&gt;-&lt;/code&gt;, and &lt;code&gt;_&lt;/code&gt; allowed. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[^-_]\.&lt;/code&gt; -&amp;gt; &lt;code&gt;-&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt; cannot be placed before &lt;code&gt;.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[a-z0-9]+$&lt;/code&gt; -&amp;gt; One or more of lowercase letters and numbers are allowed until the end of string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's still difficult, but it's easier when you break down the expression. &lt;/p&gt;

&lt;h3&gt;
  
  
  Password requirement regex
&lt;/h3&gt;

&lt;p&gt;Here's other example that I use for password validation.&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="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;(?&lt;/span&gt;&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;])(?&lt;/span&gt;&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;])(?&lt;/span&gt;&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="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)(?&lt;/span&gt;&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="p"&gt;[@&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;!%*&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Za&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="o"&gt;!%*&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,}&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;?=&lt;/code&gt; is positive lookahead assertion that checks if the pattern inside the parentheses matches. In this first block of regex, it checks whether it has at least one lowercase letter. Next block checks if it has at least one uppercase letter. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\d&lt;/code&gt; represents numerical characters which are same as &lt;code&gt;[0-9]&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Next block checks if it has at least one of following special characters &lt;code&gt;@$!%*?&amp;amp;&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Last block validates the string consists of alphanumerical characters and special characters (i.e. &lt;code&gt;@$!%*?&amp;amp;&lt;/code&gt;) and its length must be at least 8 characters long. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Email validation
&lt;/h3&gt;

&lt;p&gt;Here's another one for email validation.&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="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.]&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;@([&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&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="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;-&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;4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[\w-\.]&lt;/code&gt; means it only allows one or more of word characters, &lt;code&gt;-&lt;/code&gt;, and &lt;code&gt;.&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;([\w-]+\.)&lt;/code&gt; checks whether the pattern consists of word characters and &lt;code&gt;-&lt;/code&gt; only followed by one &lt;code&gt;.&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[\w-]{2,4}&lt;/code&gt; is the pattern that has only word characters and &lt;code&gt;-&lt;/code&gt; and its length is between 2 and 4. &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Regular expression is hard even for very experienced developers. And you don't want to use the one for production that is not proven by thousands of users. I had an experience that I couldn't add my website address on one of the biggest Canadian companies's website because my website address is treated invalid. It was frustrating. So it's always a better idea to use solutions that already exists and have been proven by many users. You can use a library like &lt;code&gt;validator&lt;/code&gt; although you still have to add your own logic to customize it. Also there are better libraries for specific types of validation such as IP address, zip code, etc. But it's good to keep trying to read regex and practice so that you can add your own logic based on existing one. &lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>productivity</category>
      <category>thankyou</category>
      <category>community</category>
    </item>
    <item>
      <title>Useful JavaScript / TypeScript Techniques</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Sat, 11 Feb 2023 06:04:09 +0000</pubDate>
      <link>https://dev.to/genne23v/using-import-export-in-javascript-1dlg</link>
      <guid>https://dev.to/genne23v/using-import-export-in-javascript-1dlg</guid>
      <description>&lt;p&gt;I had a lot of technical learning through code review this week. It's been over 2 years since I started JavaScript and I'm still very new to TypeScript. As a beginner, I've been trying to make something work, but often ignored to learn some of basics. I haven't felt the strong need of these techniques that I'm about to share. I think it's very useful to write better code. &lt;/p&gt;

&lt;h3&gt;
  
  
  Using import / export
&lt;/h3&gt;

&lt;p&gt;JavaScript has many different forms of &lt;code&gt;import&lt;/code&gt; / &lt;code&gt;export&lt;/code&gt;. Although I can easily find the documentation how to use it, it's hard to remember at the beginning, then I started to understand through experiences. My very first question was when to use &lt;code&gt;export const&lt;/code&gt; vs. &lt;code&gt;export default&lt;/code&gt;. Here are differences. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can export multiple element using &lt;code&gt;export const&lt;/code&gt; and element name cannot be changed&lt;/li&gt;
&lt;li&gt;You can export only one element using &lt;code&gt;export default&lt;/code&gt; and the name can be changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's matching &lt;code&gt;export&lt;/code&gt; and &lt;code&gt;import&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&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;import&lt;/span&gt; &lt;span class="nx"&gt;Element&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;./Element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MyName&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;./Element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Element&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;./Element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;//It works&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Element&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;./Element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;      &lt;span class="c1"&gt;//Return empty object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also you will face a situation that you want to use the same name for different modules. I had the exact case that I need to create a record in DNS module and database. Here's how you can use the same name while avoiding name conflict.&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dns&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;./dnsModule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;db&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;./database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;//You can use same name methods as below&lt;/span&gt;
&lt;span class="nx"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRecord&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I often use long descriptive function names. But I think the function names are better when they are concise and intuitive as possible. In above case, it does the same action &lt;code&gt;create a record&lt;/code&gt; in different modules. Technically they are not the same thing, but it's easily understandable and intuitive for anyone with basic knowledge of programming. &lt;/p&gt;

&lt;h3&gt;
  
  
  Reducing parameters by passing object
&lt;/h3&gt;

&lt;p&gt;I'm familiar with creating a class or an object in other languages like Java, C++, etc. I learned that TypeScript makes this much easier. You can declare a type by using keyword &lt;code&gt;interface&lt;/code&gt; or &lt;code&gt;type&lt;/code&gt;. I like the convenience that I can make some of properties optional and I can combine two different interfaces.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;displacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;fuelType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Wheel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;     &lt;span class="c1"&gt;//Optional property&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Engine&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Wheel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;//Car has all of Engine and Wheel properties&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find more examples of using &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;interface&lt;/code&gt; in this &lt;a href="https://blog.logrocket.com/types-vs-interfaces-in-typescript/" rel="noopener noreferrer"&gt;article&lt;/a&gt;. Official TypeScript website provides playground to practice how it works. &lt;a href="https://www.typescriptlang.org/play#example/types-vs-interfaces" rel="noopener noreferrer"&gt;type vs interface in TypeScript&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;You can practice coding on your own, but I think learning is faster when I work in a company or participate in open source community. It's obvious that I can't have any advice if I work alone. Looking at other's code is very helpful too. I'm looking forward to next contribution to &lt;code&gt;Starchart&lt;/code&gt; project. &lt;/p&gt;

</description>
      <category>webperf</category>
      <category>analytics</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Lesson Learned From Code Review</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Fri, 03 Feb 2023 16:46:26 +0000</pubDate>
      <link>https://dev.to/genne23v/lesson-learned-from-code-review-2ig2</link>
      <guid>https://dev.to/genne23v/lesson-learned-from-code-review-2ig2</guid>
      <description>&lt;p&gt;I had rounds of code reviews for my PR to add Route53 DNS module to &lt;code&gt;Starchart&lt;/code&gt;. Here's what I learned from the code review. &lt;/p&gt;

&lt;h3&gt;
  
  
  Working code is not enough, it should be easily readable for other developers
&lt;/h3&gt;

&lt;p&gt;I wrote a &lt;code&gt;createRecord&lt;/code&gt; function that creates a record in Route53 hosted zone. At the beginning, it didn't have &lt;code&gt;hostedZoneId&lt;/code&gt; as I was thinking we would use only one hosted zone and I could treat it as an environment variable. Then I needed ability to set a hosted zone for testing purpose and added &lt;code&gt;hostedZoneId&lt;/code&gt; to the function as the last parameter. But this is not a good practice. Some people would think &lt;code&gt;hostedZoneId&lt;/code&gt; is something that exists inside a record. It works same if I add &lt;code&gt;hostedZoneId&lt;/code&gt; in any order in the function, but the code should look clearer for others to understand. &lt;/p&gt;

&lt;h3&gt;
  
  
  Don't let same logic duplicated on the code
&lt;/h3&gt;

&lt;p&gt;Even though I know this rule and try not to write same logic in different functions, but it occurs when I don't look at my working code closely again. Duplicate code can be reduced by reviewing and refactoring multiple times before pushing it to repo. Again working code is not enough. Repeating code will result in bugs once someone fails to modify all of the same logic. Good refactoring skills come after a lot of practices. Once you confirm that the code is working, it's time to find places to improve. &lt;/p&gt;

&lt;h3&gt;
  
  
  Function should do only one thing whenever possible
&lt;/h3&gt;

&lt;p&gt;This is a good principle to write good tests or write tests more easily. Also it's good to produce the right error message when something breaks. Function name should be as clear as possible. &lt;code&gt;validate()&lt;/code&gt; does not give any clue what to validate. If it can be broken down to &lt;code&gt;validateRecord()&lt;/code&gt; and &lt;code&gt;validateDomain()&lt;/code&gt;, it will be much easier to read and probably people don't have to look into function implementation. &lt;/p&gt;

&lt;h3&gt;
  
  
  Don't use unnecessary syntax and use &lt;code&gt;linter&lt;/code&gt; to set a rule to maintain consistency
&lt;/h3&gt;

&lt;p&gt;Which code looks cleaner and easier to read?&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;foo&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;y&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;return&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;z&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;Both work same, but we don't want to write unnecessary statement. We can avoid unnecessary syntax or error-prone code by using &lt;code&gt;linter&lt;/code&gt;. It will help you get used to certain principles. &lt;/p&gt;

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

&lt;p&gt;You will look much more professional if you look through your code and improve before someone reviews. Also thoroughly take reviewer's comments and discuss if you have a different opinion. Opinions are always debatable. You will learn more from discussion. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>react</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Create Domains Using Route 53 SDK</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Thu, 26 Jan 2023 01:00:20 +0000</pubDate>
      <link>https://dev.to/genne23v/create-domains-using-route-53-sdk-2a8h</link>
      <guid>https://dev.to/genne23v/create-domains-using-route-53-sdk-2a8h</guid>
      <description>&lt;p&gt;The team for &lt;code&gt;Starchart&lt;/code&gt; project is in planning phase. It's not as exciting as coding. But research is important and it's good time to explore technologies that I have not tried or learn more details about things that I tried. &lt;code&gt;Starchart&lt;/code&gt; will create a domain for Seneca students. To do so, the team will use &lt;code&gt;Route 53&lt;/code&gt; and I spent some time on understanding &lt;code&gt;AWS SDK&lt;/code&gt; for &lt;code&gt;Route 53&lt;/code&gt; since we need to automate the steps to create a domain in &lt;code&gt;Starchart&lt;/code&gt; web app. &lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Route 53 SDK
&lt;/h3&gt;

&lt;p&gt;To learn &lt;code&gt;Route 53 SDK&lt;/code&gt;, I started with &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-route-53/index.html" rel="noopener noreferrer"&gt;official SDK page&lt;/a&gt;. To use &lt;code&gt;Route 53 SDK&lt;/code&gt;, you need to install &lt;code&gt;SDK&lt;/code&gt; by running &lt;code&gt;npm install @aws-sdk/client-route-53&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now I need to start coding. First step is import &lt;code&gt;Route53Client&lt;/code&gt; and set up configuration. &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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Route53Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-route-53&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;accessKeyId&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="na"&gt;secretAccessKey&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="na"&gt;sessionToken&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="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You need to get your own credential information from your AWS account. I use student account which might be different from normal users. I can get my &lt;code&gt;accessKeyId&lt;/code&gt;, &lt;code&gt;secretAccessKey&lt;/code&gt;, and &lt;code&gt;sessionToken&lt;/code&gt; in AWS Details as shown in below picture. &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%2Fpqbln6sdpwbzkyxripen.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%2Fpqbln6sdpwbzkyxripen.png" alt="AWS Learner Lab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Hosted Zone
&lt;/h3&gt;

&lt;p&gt;The first method that I need to try is &lt;code&gt;CreateHostedZoneCommand&lt;/code&gt;. Hosted zone is a AWS term that represents a collection of records that can be managed together, belonging to single parent domain name. And here's code snippet to use &lt;code&gt;CreateHostedZoneCommand&lt;/code&gt;. &lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Route53Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateHostedZoneCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-route-53&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;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;CallerReference&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test.starchart.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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;Route53Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&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;CreateHostedZoneCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;To create a hosted zone, all you need is &lt;code&gt;CallerReference&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt;. You can also set up more &lt;code&gt;input&lt;/code&gt; parameters such as &lt;code&gt;HostedZoneConfig&lt;/code&gt;, &lt;code&gt;VPC&lt;/code&gt;, etc. based on your need. &lt;code&gt;CallerReference&lt;/code&gt; must a unique value for your request. If you use the same &lt;code&gt;CallerReference&lt;/code&gt;, you will get an error. And &lt;code&gt;Name&lt;/code&gt; is for your domain name. If response is successful, you will receive metadata of your request, location, change info, hosted zone info, nameserver info. &lt;/p&gt;

&lt;p&gt;Now I have records inside the hosted zone that I created. &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%2Fvwnu7yr0r1833monw4ej.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%2Fvwnu7yr0r1833monw4ej.png" alt="Hosted zone details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add/Update/Delete  Records in Hosted Zone
&lt;/h3&gt;

&lt;p&gt;I can add a new record or update existing records with &lt;code&gt;ChangeResourceRecordsSetCommand&lt;/code&gt; function. Below is the basic syntax how to create or update your record. &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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Route53Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ChangeResourceRecordSetsCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-route-53&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;changeBatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&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;UPSERT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ResourceRecordSet&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;mydomain.starchart.com&lt;/span&gt;&lt;span class="dl"&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;A&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;TTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ResourceRecords&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;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;192.168.0.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="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ChangeBatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;changeBatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;HostedZoneId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;Route53Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&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;ChangeResourceRecordSetsCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 


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

&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;input&lt;/code&gt; parameter, you need to provide &lt;code&gt;changeBatch&lt;/code&gt; object and &lt;code&gt;HostedZoneId&lt;/code&gt;. In this case, I use &lt;code&gt;UPSERT&lt;/code&gt; action. This is a way to simplify the method that it can add a new record if it doesn't exist or update existing one. In the &lt;code&gt;ResourceRecordSet&lt;/code&gt;, you should provide domain name, type, TTL (i.e. record cache time to live), and route traffic to. In this example, the new domain &lt;code&gt;mydomain.starchart.com&lt;/code&gt; will be directed to &lt;code&gt;A record&lt;/code&gt;, &lt;code&gt;192.168.0.1&lt;/code&gt;. You can find more options when you set routing policy other than this simple example. I found &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/route53/change-resource-record-sets.html" rel="noopener noreferrer"&gt;AWS CLI Command Reference page&lt;/a&gt; is very helpful to understand the parameters and refer to other &lt;code&gt;ChangeBatch&lt;/code&gt; syntax. &lt;/p&gt;

&lt;h3&gt;
  
  
  List Records and View Change Status
&lt;/h3&gt;

&lt;p&gt;When you fetch all your records or partial records, you can use &lt;code&gt;ListResourceRecordSetsCommand&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Lastly &lt;code&gt;GetChangeCommand&lt;/code&gt; method can check the status of specific &lt;code&gt;ChangeBatch&lt;/code&gt; by providing &lt;code&gt;ChangeInfo.Id&lt;/code&gt; from &lt;code&gt;ChangeResourceRecordSetsCommand&lt;/code&gt; response as a parameter. &lt;code&gt;GetChangeCommand&lt;/code&gt; returns &lt;code&gt;ChangeInfo&lt;/code&gt; and &lt;code&gt;ChangeInfo.Status&lt;/code&gt; will show either &lt;code&gt;PENDING&lt;/code&gt; or &lt;code&gt;INSYNC&lt;/code&gt;. &lt;/p&gt;

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

&lt;p&gt;I used AWS S3, DynamoDB SDK before. So it didn't take much time to set up and configure. AWS SDK syntax is highly consistent and it's easy to use once you learn any one of these SDKs. As I mentioned above, AWS CLI reference page has more details explaining each parameter and response although you will rely on JavaScript SDK page more to write your code.  Now I'm eager to write code for &lt;code&gt;Starchart&lt;/code&gt;. &lt;/p&gt;

</description>
      <category>awsroute53</category>
      <category>starchart</category>
      <category>senecaosd700</category>
      <category>route53sdk</category>
    </item>
    <item>
      <title>How to Write Dockerfile and Basic Docker Commands</title>
      <dc:creator>Genne23v</dc:creator>
      <pubDate>Mon, 23 Jan 2023 20:54:31 +0000</pubDate>
      <link>https://dev.to/genne23v/how-to-write-dockerfile-and-basic-docker-commands-6i3</link>
      <guid>https://dev.to/genne23v/how-to-write-dockerfile-and-basic-docker-commands-6i3</guid>
      <description>&lt;p&gt;Seneca OSD700 team project &lt;code&gt;Starchart&lt;/code&gt; will use many &lt;code&gt;Docker&lt;/code&gt; containers. I would like to summarize how to write &lt;code&gt;Dockerfile&lt;/code&gt; and basic &lt;code&gt;Docker&lt;/code&gt; commands. &lt;/p&gt;

&lt;p&gt;First we need to write a &lt;code&gt;Dockerfile&lt;/code&gt; which is a set of instruction to build a &lt;code&gt;Docker&lt;/code&gt; image. A &lt;code&gt;Docker&lt;/code&gt; image is a read-only template with instructions for creating a Docker container and the container is a runnable instance of an image. It's probably better to understand with below &lt;code&gt;node&lt;/code&gt; project example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:16.15.1&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="Wonkeun No &amp;lt;wonkeun.no@gmail.com&amp;gt;"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT=8080&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./src ./src&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; npm start&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt; must begin with &lt;code&gt;FROM&lt;/code&gt; to specify what parent image the container uses. In this case, I use &lt;code&gt;node&lt;/code&gt; version &lt;code&gt;16.15.1&lt;/code&gt;. So my app will be running on top of &lt;code&gt;node&lt;/code&gt; image which someone built for everybody. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;LABEL&lt;/code&gt; is not a mandatory command, but you can add some metadata information about the image as a &lt;code&gt;key=value&lt;/code&gt; pair. You can add more like &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, etc. &lt;/p&gt;

&lt;p&gt;Environment variable can be set with &lt;code&gt;ENV&lt;/code&gt; command. It can be overridden with &lt;code&gt;docker&lt;/code&gt; command. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; sets the working directory. Multiple &lt;code&gt;WORKDIR&lt;/code&gt; is also possible. When a relative path is provided, it will be relative to previous &lt;code&gt;WORKDIR&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Then, copy &lt;code&gt;package.json&lt;/code&gt; and paste it to &lt;code&gt;./&lt;/code&gt; directory  with &lt;code&gt;COPY&lt;/code&gt; instruction. And run &lt;code&gt;npm install&lt;/code&gt; with &lt;code&gt;RUN&lt;/code&gt; to download npm dependencies in this case. &lt;/p&gt;

&lt;p&gt;Finally copy all &lt;code&gt;./src&lt;/code&gt; files and paste into container's &lt;code&gt;./src&lt;/code&gt; directory and run &lt;code&gt;npm start&lt;/code&gt; with &lt;code&gt;CMD&lt;/code&gt; with port 8080 open outside of container. You can run many &lt;code&gt;RUN&lt;/code&gt; instructions, but &lt;code&gt;CMD&lt;/code&gt; can be run once as a container launch command. You can still write multiple &lt;code&gt;CMD&lt;/code&gt;, but all will be ignored by last &lt;code&gt;CMD&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Once &lt;code&gt;Dockerfile&lt;/code&gt; is written, it's highly recommended to ignore some of files that are not necessary in &lt;code&gt;docker build&lt;/code&gt; by adding them to &lt;code&gt;.dockerignore&lt;/code&gt;. This process helps to reduce the size of image and build faster. You secret information files should not be in the image either. &lt;/p&gt;

&lt;p&gt;There is more information about &lt;code&gt;Dockerfile&lt;/code&gt; in the official &lt;code&gt;Docker&lt;/code&gt; site. &lt;a href="https://docs.docker.com/engine/reference/builder"&gt;Docker reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's ready to build the image with below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;image&amp;gt;:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-t&lt;/code&gt; can specify a tag for the image. This option can be combined with &lt;code&gt;-i&lt;/code&gt; (i.e. shortened form of &lt;code&gt;--interactive&lt;/code&gt;) to interact with container in a terminal. It's useful to see what is going on inside the container.  &lt;/p&gt;

&lt;p&gt;If the build is successful, you can find the image with below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image &lt;span class="nb"&gt;ls&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's time to actually run the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &amp;lt;image&amp;gt; &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &amp;lt;image&amp;gt;:latest &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's explanation for each option.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--rm&lt;/code&gt; removes the container when it exits automatically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name&lt;/code&gt; assign a name to the container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--env-file&lt;/code&gt; specifies a file that defines environment variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e&lt;/code&gt; sets an environment variable for the container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; binds the host machine's port with container's port (i.e. [HOST_MACHINE_PORT:CONTAINER_PORT]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt; runs the container in background and print container ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you run above command, you will see the container's ID. Run below to see the logs from the container. &lt;code&gt;-f&lt;/code&gt; option is to continuously print out the logs as it generates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;container_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This article helps to create a &lt;code&gt;Docker&lt;/code&gt; image and run a single container. I will write more about best practises of writing &lt;code&gt;Dockerfile&lt;/code&gt; , &lt;code&gt;DockerHub&lt;/code&gt;, and &lt;code&gt;docker-compose&lt;/code&gt;.  &lt;/p&gt;

</description>
      <category>docker</category>
      <category>dockerfile</category>
    </item>
  </channel>
</rss>
