<?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: Honza</title>
    <description>The latest articles on DEV Community by Honza (@crs1138).</description>
    <link>https://dev.to/crs1138</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%2F96060%2Ffc80f57a-135e-45b5-b0eb-fd10e0e9d396.jpg</url>
      <title>DEV Community: Honza</title>
      <link>https://dev.to/crs1138</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/crs1138"/>
    <language>en</language>
    <item>
      <title>Creating HelloWorld CLI Command with NodeJS</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Wed, 23 Aug 2023 19:14:02 +0000</pubDate>
      <link>https://dev.to/crs1138/creating-helloworld-cli-command-with-nodejs-2hfd</link>
      <guid>https://dev.to/crs1138/creating-helloworld-cli-command-with-nodejs-2hfd</guid>
      <description>&lt;p&gt;I have recently returned from the World Congress conference organised by WeAreDevelopers in Berlin. It's been the second time I attended and I saw some very inspiring talks. One of them caught my attention in particular. It was Phil Nash's (Sonar) talk titled &lt;em&gt;4 Steps from Javascript to Typescript&lt;/em&gt;. Apart from the lesson on Typescript, he was using NodeJS to create a CLI command.&lt;/p&gt;

&lt;p&gt;Now, that's nothing unseen before, we all use various CLI commands written in JS (think npm, trash-cli, etc.), but it has never occurred to write my own. In the past, when I needed a CLI script, I wrote it in ZSH. However, one of them was getting a bit too complex that I caught myself thinking: "Next time I'll write this in another language." At Phil's talk, I had that a-ha moment. Of course, I can use JS!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brief
&lt;/h2&gt;

&lt;p&gt;This is my first little exercise to learn the ins and outs of creating a CLI command in NodeJS. This is the brief I gave myself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a CLI command that accepts zero to many arguments and displays:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;"Hello world!" for no arguments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Hello [name]!" if one argument is provided.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Hello [name1] and [name2]!" for two arguments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Hello [name1], [name2], … [nameX]!" for three and more arguments.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nodejs.org/"&gt;Node&lt;/a&gt; – get some recent version, 18+, it will likely work on ancient versions too, but if you have to use an old version of Node, you probably know what you're doing anyway.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pnpm.io/installation"&gt;PNPM&lt;/a&gt; – Node package manager&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  TLDR;
&lt;/h3&gt;

&lt;p&gt;For those of you who are results-oriented, you can &lt;a href="https://github.com/crs1138/hello-world-cli"&gt;find the repo on my Github account&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Node package
&lt;/h3&gt;

&lt;p&gt;First I've created a new Node package. Nowadays, my package manager of choice is &lt;a href="https://pnpm.io/"&gt;&lt;code&gt;pnpm&lt;/code&gt;&lt;/a&gt; – it's fast, more performant and takes up less space on the disk thanks to the way it deals with Node modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create and enter a new folder&lt;/span&gt;
take hello-world-cli
&lt;span class="c"&gt;# Initialise a new Node package&lt;/span&gt;
pnpm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;em&gt;package.json&lt;/em&gt; file with the basic default metadata. We will need that file for installing our dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic file structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./
├── LICENSE
├── README.md
├── bin
│   └── hello
├── node_modules
├── package.json
├── pnpm-lock.yaml
└── src
    ├── cli.js
    └── greetings.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The CLI command &lt;code&gt;hello&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, we create the file &lt;em&gt;hello&lt;/em&gt;: &lt;code&gt;touch /bin/hello&lt;/code&gt;. This is gonna be the actual command that the user will launch. Make sure to make it executable &lt;code&gt;chmod a+x ./bin/hello&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="cp"&gt;#!/usr/bin/env node
&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;run&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;../src/cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;run&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;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see I specify the &lt;a href="https://en.wikipedia.org/wiki/Shebang_(Unix)"&gt;shebang&lt;/a&gt; to execute this file using node as an interpreter. This enables us to use JS to import the &lt;code&gt;run()&lt;/code&gt; function from the &lt;code&gt;/src/cli.js&lt;/code&gt; and call it asynchronously passing the array of arguments (&lt;a href="https://nodejs.org/docs/latest/api/process.html#processargv"&gt;&lt;code&gt;process.argv&lt;/code&gt;&lt;/a&gt;) from the command line as its parameter. The first element &lt;code&gt;process.argv[0]&lt;/code&gt; is read-only and is usually reserved for the absolute path of the processor (in my case: &lt;code&gt;/Users/myuser/.nvm/versions/node/v20.5.1/bin/node&lt;/code&gt;. The second array item &lt;code&gt;process.argv[1]&lt;/code&gt; is the path to the command we're running: &lt;code&gt;/Users/myuser/Sites/_tuts/hello-word-cli/hello&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In case there's an error, I catch it and display it in the console.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;run()&lt;/code&gt; – process the command
&lt;/h3&gt;

&lt;p&gt;First of all, let's install &lt;a href="https://www.npmjs.com/package/yargs"&gt;yargs&lt;/a&gt; package to make our life easier processing all the possible parameters: &lt;code&gt;pnpm install yargs&lt;/code&gt;. I will touch just the surface of what this package can do for you but, this is a good template even for more complex CLI commands.&lt;/p&gt;

&lt;p&gt;This is the code in &lt;code&gt;/scr/cli.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;yargs&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hideBin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;yargs/helpers&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;showGreeting&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;./greetings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;yargs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hideBin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;showGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I import the &lt;code&gt;yargs()&lt;/code&gt; function and its helper function &lt;code&gt;hideBin()&lt;/code&gt;. The &lt;code&gt;hideBin(args)&lt;/code&gt; is a shorthand for &lt;code&gt;args.slice(2)&lt;/code&gt;. This way, you can make your command work even in not-so-standard environments like &lt;a href="https://github.com/electron/electron/issues/4690"&gt;Electron&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I define the asynchronous function &lt;code&gt;run(args)&lt;/code&gt; that takes an array as its argument. Process this argument using the &lt;code&gt;yargs()&lt;/code&gt; and &lt;code&gt;hideBin()&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;I desctructure the &lt;code&gt;_&lt;/code&gt; property from the &lt;code&gt;argv&lt;/code&gt; returned by &lt;code&gt;yargs&lt;/code&gt; and rename this variable as &lt;code&gt;names&lt;/code&gt; for the convenience of my code and pass the &lt;code&gt;names&lt;/code&gt; to my &lt;code&gt;showGreeting(names)&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;That's it! As far as the processing of a CLI command is concerned, we're done. The rest of it is just plain JS as usual.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;showGreeting(names)&lt;/code&gt; - display the right greeting
&lt;/h3&gt;

&lt;p&gt;The file &lt;code&gt;/src/greetings.js&lt;/code&gt; serves as a library of functions related to displaying greetings.&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;joinTwoNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;twoNames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;twoNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; and &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLongGreetings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;longListOfNames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;longListOfNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&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;currentIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;longListOfNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;acc&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&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="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; and &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="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;getGreetings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&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;greetingsList&lt;/span&gt; &lt;span class="o"&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;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;`Hi there &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;names&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="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;`Hi there &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;joinTwoNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&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="s2"&gt;`Hi there &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;getLongGreetings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingsIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;greetingsList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;greetingsIndex&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showGreeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&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;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getGreetings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;getGreetings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;showGreeting&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;I export the &lt;code&gt;showGreeting()&lt;/code&gt; function as well as &lt;code&gt;getGreetings()&lt;/code&gt; for convenience even though I don't use &lt;code&gt;getGreetings()&lt;/code&gt; anywhere else for now.&lt;/p&gt;

&lt;p&gt;There is a list of string templates saved in the &lt;code&gt;getGreetings(names)&lt;/code&gt; function that is used to decide which greeting to use depending on how many names are passed as its argument.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;No names? Fine, it's just &lt;code&gt;Hello world!&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One name? Easy, &lt;code&gt;Hi there, ${names[0]}&lt;/code&gt; will print the first (and only) item of the &lt;code&gt;names&lt;/code&gt; array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For two names, I have a helper function &lt;code&gt;joinTwoNames(twoNames)&lt;/code&gt; that concatenates the two names in the array adding the &lt;code&gt;and&lt;/code&gt; in between them and this is then used in the &lt;code&gt;greetingList[2]&lt;/code&gt; string template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any more than three names will end up using the &lt;code&gt;getLongGreetings(longListOfNames)&lt;/code&gt; function that will rely on the high-order method &lt;code&gt;array.prototype.reduce()&lt;/code&gt; to concatenate all individual names with a comma as its separator apart from the last two that will be linked by the conjunction &lt;code&gt;and&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  That's it, folks!
&lt;/h2&gt;

&lt;p&gt;I hope you found this useful, please let me know if you have any questions or comments. I intended to demonstrate in a simple and easy-to-understand way the basics of creating a simple CLI command in Node.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover image by Emma Plunkett © 2016&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>beginners</category>
      <category>cli</category>
    </item>
    <item>
      <title>`allUnique()` characters – Interview Question</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Mon, 06 Jun 2022 17:51:56 +0000</pubDate>
      <link>https://dev.to/crs1138/allunique-characters-9ck</link>
      <guid>https://dev.to/crs1138/allunique-characters-9ck</guid>
      <description>&lt;p&gt;A few weeks ago I subscribed to Cassidy's newsletter. I have been struggling with my constantly overflowing email accounts for a couple of years now. Thus, I'm a bit picky with what I sign up for. This one is different! I love the format. I love that it is to the point whilst delivering an interesting selection of reading every week. It's witty, it's funny and it's interesting. Give it a try: have a &lt;a href="https://click.pstmrk.it/2sm/cassidoo.co%2Fnewsletter%2F/etBx8C0N/bP1H/XTJUF4u4Yq/OGIyZDRjMjctMTAzMy00Y2Y2LWI2YWMtZjVlNTRiOWFmNDA1"&gt;rendezvous with cassidoo&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Task description
&lt;/h2&gt;

&lt;p&gt;In each of her newsletters, Cassidy publishes an &lt;em&gt;Interview Question of the week&lt;/em&gt;. A short code quiz for developers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Write a function that determines if all the characters in a given string are unique. Can you do this without making any new variables? You choose if you want to include capitalization in your consideration for this one, as a fun challenge.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Example:&lt;/em&gt;&lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cassidy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat &amp;amp; dog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat+dog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;— Cassidy's newsletter, the 30&lt;sup&gt;th&lt;/sup&gt; of May , 2022&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I get it, life's too short. Here are my three ways of solving the quiz:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/cassidoo-allunique-reduce-false-when-duplicate-letter-found-p1nsjf"&gt;The &lt;code&gt;Array.prototype.reduce()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/cassidoo-allunique-while-false-when-duplicate-letter-found-dp3y1i"&gt;The &lt;code&gt;while()&lt;/code&gt; loop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/cassidoo-allunique-string-iterator-w89gxm"&gt;The &lt;code&gt;String.prototype[@iterator]()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;I'll be honest. I'm still getting my head around writing tests first. This is an easy quiz with clearly defined conditions. I took it on the chin and wrote my tests first like the good boy I am! Woof! Originally, I was just going to do one solution for the quiz. As I managed to do so pretty fast, I decided to roll out a few other ways to solve the quiz. This is when I found the tests extremely handy. I didn't have to check whether my other solutions worked on not. I &lt;em&gt;knew&lt;/em&gt; it straight away when the tests passed.&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;allUnique&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index&lt;/span&gt;&lt;span class="dl"&gt;"&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="s2"&gt;Strings of unique characters&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return `false` if there are present the same characters in the string&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cassidy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cat &amp;amp; dog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crs1138&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return `true` if no character is repeated&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cat+dog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcdefghijklmnopqrstuvwxyzA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abcdefghijklmnopqrstuvwxyz 1234567890&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution 1 – the &lt;code&gt;Array.prototype.reduce()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;My primary weapon of choice was ES6 and its higher-order function &lt;code&gt;reduce()&lt;/code&gt;. I split the string into individual characters in an array. In the callback function, I check if that character was already present in the array. If not, I added it to the &lt;code&gt;acc&lt;/code&gt; accumulator. In the end, I compare the length of the original string and the length of the array with unique characters to decide whether to return &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I use &lt;code&gt;arr.splice(1)&lt;/code&gt; to break out from the &lt;code&gt;reduce&lt;/code&gt; callback loop when a duplicate character has been found. &lt;code&gt;arr.splice(1)&lt;/code&gt; mutates the original array the reduce was launched on. The mutated array has only one item and therefore the callback loop is stopped. In this case I don't mind the mutation of the array as it is only to point out a repeated character.&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="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&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;//break out of reduce early&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acc&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="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I noticed that I'm checking twice for the presence of the &lt;em&gt;next&lt;/em&gt; character – &lt;code&gt;acc.includes(next))&lt;/code&gt;. A little refactor and tadaaa – here is my first result with the callback on one line:&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;function&lt;/span&gt; &lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splice&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;next&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="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution 2 – the &lt;code&gt;while()&lt;/code&gt; loop
&lt;/h2&gt;

&lt;p&gt;Another solution that sprung up to my mind is the classic ES5 way. Just use the &lt;code&gt;while()&lt;/code&gt; loop, Luke!&lt;/p&gt;

&lt;p&gt;The algorithm is pretty straight forward. Break the string into an array of individual characters. Declare an empty array for &lt;code&gt;uniques&lt;/code&gt;    and loop over all the characters. If the &lt;code&gt;char&lt;/code&gt; is unique add it to the &lt;code&gt;uniques&lt;/code&gt; array. If it finds a repeated character &lt;code&gt;return false&lt;/code&gt;, otherwise after going through all of them &lt;code&gt;return true&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pop&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;uniques&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;uniques&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have not used the &lt;code&gt;Array.prototype.indexOf()&lt;/code&gt; in donkey's years. It seems a bit archaic in comparison with the &lt;code&gt;Array.prototype.includes()&lt;/code&gt;. There was no extra challenge in this solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3 – the &lt;code&gt;String.prototype[@@iterator]()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;It occurred to me that I could learn/practice something I don't do everyday. I went for the &lt;code&gt;String.prototype[@@iterator]()&lt;/code&gt; method.&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="nx"&gt;allUnique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iterator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iterator&lt;/span&gt;&lt;span class="p"&gt;]();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iterator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&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;uniques&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;uniques&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;char&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;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iterator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First of all I create an &lt;em&gt;iterator&lt;/em&gt; object &lt;code&gt;const iterator = str[Symbol.iterator]()&lt;/code&gt;. This object has one method &lt;code&gt;iterator.next()&lt;/code&gt;. When called it returns the next character in the &lt;code&gt;str&lt;/code&gt; string.&lt;/p&gt;

&lt;p&gt;I call &lt;code&gt;let char = iterator.next()&lt;/code&gt; for the first time declaring the character object. The object has two properties: &lt;code&gt;char.value&lt;/code&gt; containing the value of the currently iterated character and a boolean &lt;code&gt;char.done&lt;/code&gt; signalling whether there is another character for the iterator to go to if I was to call &lt;code&gt;iterator.next()&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;I prepare my string for unique characters – &lt;code&gt;uniques&lt;/code&gt; and start looping over checking for the current character value being already present in the string of unique characters. If that happens I break out of the loop. Otherwise, I add the character to the &lt;code&gt;uniques&lt;/code&gt; and move on to the next one.&lt;/p&gt;

&lt;p&gt;After the loop has finished one way or another I compare the lengths of the original string with the collected unique characters and return &lt;code&gt;true&lt;/code&gt; if they are equal or &lt;code&gt;false&lt;/code&gt; when they are not.&lt;/p&gt;

&lt;p&gt;The specs for the &lt;code&gt;String.prototype[@@iterator]()&lt;/code&gt; method are worth reading as this is a rarely used concept for many devs, myself included.&lt;/p&gt;

&lt;h2&gt;
  
  
  And the winner is…
&lt;/h2&gt;

&lt;p&gt;So, I've got three solutions but which one is &lt;em&gt;truly the best&lt;/em&gt;? As the truth is in the eye of the beholder, thus it depends on the definition of &lt;em&gt;best&lt;/em&gt;. Some might argue that the first solution (&lt;code&gt;reduce()&lt;/code&gt;) is the shortest, others could say the second solution (&lt;code&gt;while()&lt;/code&gt;) is the easiest to read, whilst another group could vouch for the third solution being the most modern (whatever that might mean).&lt;/p&gt;

&lt;p&gt;I decided to compare the performance of each solution. There are various benchmark tools to measure JS performance available online. I tried three that came up on my Google search results. They all have similar UI, letting me put all three solutions next to each other. I defined a bunch of shorter and longer strings, including two with all of the basic keyboard characters (one with and the other one without repetition). When I launched the test it runs each solution extensively and measure how many operations per second it was able to execute. The bigger number the better as it means fewer resources are needed to run the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jsbench.me/sal411o06u/2"&gt;jsbench.me results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jsben.ch/nLA41"&gt;jsben.ch results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://perf.link/#eyJpZCI6Imt5MjdibGh3aWNwIiwidGl0bGUiOiJhbGxVbmlxdWUoJ215IHN0cmluZycpIiwiYmVmb3JlIjoiIiwidGVzdHMiOlt7Im5hbWUiOiJTdHJpbmcucHJvdG90eXBlLnJlZHVjZSgpIiwiY29kZSI6ImZ1bmN0aW9uIGFsbFVuaXF1ZShzdHIpIHtcbiAgY29uc3QgY2hhcnMgPSBzdHIuc3BsaXQoXCJcIikucmVkdWNlKChhY2MsIG5leHQsIGksIGFycikgPT4ge1xuICAgIHJldHVybiBhY2MuaW5jbHVkZXMobmV4dCkgPyBhcnIuc3BsaWNlKDEpIDogKGFjYyA9IGFjYyArIG5leHQpO1xuICB9LCBcIlwiKTtcbiAgcmV0dXJuIHN0ci5sZW5ndGggPT09IGNoYXJzLmxlbmd0aDtcbn1cblxuXG5hbGxVbmlxdWUoXCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAxMjM0NTY3ODkwXCIpO1xuYWxsVW5pcXVlKFwiSmFuIEhvbnphIFBveml2aWxcIik7XG5hbGxVbmlxdWUoXCJgMTIzNDU2Nzg5MC09cXdlcnR5dWlvcFtdXFxhc2RmZ2hqa2w7J3p4Y3Zibm0sLi8gfiFAIyQlXiYqKClfK1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6XFxcIlpYQ1ZCTk08Pj9cIik7XG5hbGxVbmlxdWUoXCJgMTIzNDU2Nzg5MC09cXdlcnR5dWlvcFtdXFxhc2RmZ2hqa2w7J3p4Y3Zibm0sLi8gfiFAIyQlXiYqKClfK1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6XFxcIlpYQ1ZCTk08Pj8gXCIpOyIsInJ1bnMiOlsxMzAwMCwzMDAwLDMwMDAsMTMwMDAsMzAwMCwxMjAwMCw4MDAwLDEyMDAwLDMwMDAsODAwMCwzMDAwLDEwMDAwLDIwMDAsMTMwMDAsNzAwMCwxMDAwLDYwMDAsNzAwMCwxMzAwMCw3MDAwLDExMDAwLDIwMDAsMzAwMCwxMjAwMCwzMDAwLDEwMDAsNjAwMCwzMDAwLDEyMDAwLDEwMDAwLDEyMDAwLDEwMDAwLDEyMDAwLDExMDAwLDcwMDAsMTEwMDAsODAwMCw1MDAwLDcwMDAsMTAwMDAsMTAwMCw0MDAwLDYwMDAsMTAwMCwxMzAwMCw3MDAwLDkwMDAsOTAwMCw2MDAwLDIwMDAsNDAwMCw0MDAwLDgwMDAsMjAwMCw4MDAwLDExMDAwLDIwMDAsMTUwMDAsMTEwMDAsMTIwMDAsMTAwMCwxMDAwLDExMDAwLDEyMDAwLDEzMDAwLDYwMDAsNzAwMCwzMDAwLDEwMDAsMTIwMDAsMzAwMCwxMDAwLDExMDAwLDEwMDAwLDIwMDAsMTAwMCwxMDAwLDIwMDAsODAwMCwxMDAwMCw2MDAwLDUwMDAsNzAwMCw5MDAwLDQwMDAsNjAwMCw4MDAwLDEwMDAsMTQwMDAsMzAwMCwxMDAwMCwxMDAwLDUwMDAsNzAwMCw2MDAwLDcwMDAsMTEwMDAsMTAwMCwxNDAwMCwxMDAwXSwib3BzIjo2ODAwfSx7Im5hbWUiOiJ3aGlsZSgpIiwiY29kZSI6ImZ1bmN0aW9uIGFsbFVuaXF1ZShzdHIpIHtcbiAgY29uc3QgY2hhcnMgPSBzdHIuc3BsaXQoXCJcIik7XG4gIGxldCB1bmlxdWVzID0gW107XG4gIHdoaWxlIChjaGFycy5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgbGV0dGVyID0gY2hhcnMucG9wKCk7XG4gICAgaWYgKHVuaXF1ZXMuaW5kZXhPZihsZXR0ZXIpID49IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgdW5pcXVlcy5wdXNoKGxldHRlcik7XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbmFsbFVuaXF1ZShcImFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6IDEyMzQ1Njc4OTBcIik7XG5hbGxVbmlxdWUoXCJKYW4gSG9uemEgUG96aXZpbFwiKTtcbmFsbFVuaXF1ZShcImAxMjM0NTY3ODkwLT1xd2VydHl1aW9wW11cXGFzZGZnaGprbDsnenhjdmJubSwuLyB%2BIUAjJCVeJiooKV8rUVdFUlRZVUlPUHt9fEFTREZHSEpLTDpcXFwiWlhDVkJOTTw%2BP1wiKTtcbmFsbFVuaXF1ZShcImAxMjM0NTY3ODkwLT1xd2VydHl1aW9wW11cXGFzZGZnaGprbDsnenhjdmJubSwuLyB%2BIUAjJCVeJiooKV8rUVdFUlRZVUlPUHt9fEFTREZHSEpLTDpcXFwiWlhDVkJOTTw%2BPyBcIik7IiwicnVucyI6WzcwMDAsMzAwMCwxMDAwLDcwMDAsMTEwMDAsNDAwMCw0MDAwLDEwMDAwLDIwMDAsNTAwMCwxMDAwLDIwMDAsMTAwMCw4MDAwLDQwMDAsNDAwMCwzMDAwLDExMDAwLDcwMDAsMjAwMCwxMTAwMCw4MDAwLDEwMDAsOTAwMCw2MDAwLDUwMDAsMTAwMCwyMDAwLDEwMDAwLDgwMDAsMTAwMCwxMDAwMCwxMDAwMCw4MDAwLDQwMDAsODAwMCw3MDAwLDMwMDAsNDAwMCw4MDAwLDEwMDAsMjAwMCw1MDAwLDIwMDAsMTAwMCw2MDAwLDUwMDAsNzAwMCwxMDAwLDYwMDAsNzAwMCw1MDAwLDQwMDAsNjAwMCw2MDAwLDIwMDAsNjAwMCwzMDAwLDkwMDAsNzAwMCw4MDAwLDMwMDAsNjAwMCwxMDAwLDQwMDAsMTAwMDAsMzAwMCw3MDAwLDQwMDAsNDAwMCw2MDAwLDEwMDAsNTAwMCw3MDAwLDcwMDAsMTAwMCw5MDAwLDEwMDAsNjAwMCw2MDAwLDMwMDAsNzAwMCw1MDAwLDYwMDAsMTAwMDAsNDAwMCw1MDAwLDEwMDAsNTAwMCw2MDAwLDEwMDAsMTAwMCw1MDAwLDIwMDAsNDAwMCw1MDAwLDQwMDAsNTAwMCwxMDAwLDcwMDBdLCJvcHMiOjQ5ODB9LHsibmFtZSI6IlN0cmluZy5wcm90b3R5cGVbQGl0ZXJhdG9yXSgpIiwiY29kZSI6ImZ1bmN0aW9uIGFsbFVuaXF1ZShzdHIpIHtcbiAgY29uc3QgaXRlcmF0b3IgPSBzdHJbU3ltYm9sLml0ZXJhdG9yXSgpO1xuICBsZXQgY2hhciA9IGl0ZXJhdG9yLm5leHQoKTtcbiAgbGV0IHVuaXF1ZXMgPSBcIlwiO1xuICB3aGlsZSAoIWNoYXIuZG9uZSkge1xuICAgIGlmICh1bmlxdWVzLmluY2x1ZGVzKGNoYXIudmFsdWUpKSB7XG4gICAgICBicmVhaztcbiAgICB9XG4gICAgdW5pcXVlcyA9IHVuaXF1ZXMgKyBjaGFyLnZhbHVlO1xuICAgIGNoYXIgPSBpdGVyYXRvci5uZXh0KCk7XG4gIH1cbiAgcmV0dXJuIHN0ci5sZW5ndGggPT09IHVuaXF1ZXMubGVuZ3RoO1xufVxuXG5hbGxVbmlxdWUoXCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAxMjM0NTY3ODkwXCIpO1xuYWxsVW5pcXVlKFwiSmFuIEhvbnphIFBveml2aWxcIik7XG5hbGxVbmlxdWUoXCJgMTIzNDU2Nzg5MC09cXdlcnR5dWlvcFtdXFxhc2RmZ2hqa2w7J3p4Y3Zibm0sLi8gfiFAIyQlXiYqKClfK1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6XFxcIlpYQ1ZCTk08Pj9cIik7XG5hbGxVbmlxdWUoXCJgMTIzNDU2Nzg5MC09cXdlcnR5dWlvcFtdXFxhc2RmZ2hqa2w7J3p4Y3Zibm0sLi8gfiFAIyQlXiYqKClfK1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6XFxcIlpYQ1ZCTk08Pj8gXCIpOyIsInJ1bnMiOlszMDAwLDMwMDAsMjAwMCwxMjAwMCw3MDAwLDYwMDAsMTAwMDAsNzAwMCwxMDAwLDYwMDAsMTIwMDAsMTAwMCw5MDAwLDEwMDAsMjAwMCwyMDAwLDEwMDAsNjAwMCw3MDAwLDgwMDAsMzAwMCw1MDAwLDEwMDAsMzAwMCwxMTAwMCwyMDAwLDEwMDAsNDAwMCwxMjAwMCw2MDAwLDEwMDAsMzAwMCw3MDAwLDYwMDAsODAwMCw4MDAwLDEwMDAwLDEwMDAwLDEyMDAwLDcwMDAsNDAwMCwxMzAwMCwzMDAwLDIwMDAsMTEwMDAsMTIwMDAsMTEwMDAsMjAwMCwxMDAwMCw1MDAwLDMwMDAsNzAwMCw1MDAwLDEwMDAwLDMwMDAsMTAwMCwxMjAwMCw0MDAwLDEwMDAwLDcwMDAsMTAwMCwzMDAwLDYwMDAsMTIwMDAsMzAwMCw0MDAwLDMwMDAsOTAwMCw4MDAwLDEwMDAsNTAwMCwyMDAwLDMwMDAsMzAwMCwyMDAwLDExMDAwLDExMDAwLDEyMDAwLDYwMDAsNDAwMCwyMDAwLDYwMDAsNDAwMCw2MDAwLDYwMDAsMjAwMCw0MDAwLDEyMDAwLDQwMDAsODAwMCw2MDAwLDYwMDAsMTAwMCwxMTAwMCw1MDAwLDMwMDAsNDAwMCwxMDAwLDEwMDAwLDgwMDBdLCJvcHMiOjU3OTB9XSwidXBkYXRlZCI6IjIwMjItMDYtMDZUMDk6NTY6NTUuODMxWiJ9"&gt;perf.link results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The worst performance out of the three solutions had in all tests the &lt;code&gt;while()&lt;/code&gt; solution. Its performance was on average around 60% lower than the most performant solution. The second-best performing solution was the &lt;code&gt;reduce()&lt;/code&gt; function, being on average around 13% lower than the fastest solution.&lt;/p&gt;

&lt;p&gt;And the winner is, drum roll… The &lt;code&gt;iterator&lt;/code&gt; solution! &lt;/p&gt;

&lt;p&gt;It needs to be said that the &lt;code&gt;iterator&lt;/code&gt; solution started to outperform the other two once the break-out condition was added to the equation. Before that, it had surprisingly poor results. My conclusion is that the &lt;code&gt;iterator&lt;/code&gt; is worth learning but I shall run performance tests for individual use cases.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover image &lt;a href="https://www.emmaplunkett.art/artwork/shoe-paisley/"&gt;Shoe Paisley by Emma Plunkett&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>interview</category>
      <category>codingchallenge</category>
      <category>learninginpublic</category>
    </item>
    <item>
      <title>My favourite learning resources for web developers</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Mon, 04 Apr 2022 15:52:18 +0000</pubDate>
      <link>https://dev.to/crs1138/my-favourite-learning-resources-for-web-developers-49ld</link>
      <guid>https://dev.to/crs1138/my-favourite-learning-resources-for-web-developers-49ld</guid>
      <description>&lt;p&gt;Recently, a discussion about learning subscription broke out on the &lt;a href="https://www.learninpublic.org/"&gt;Learning in Public&lt;/a&gt; platform. This nudged me to sum up some of my favourites. I hope you find them inspiring. I'd be thrilled if you take the time to write your chosen ones in the comments.&lt;/p&gt;

&lt;p&gt;A handpicked list of resources I found useful. They are in no particular order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Courses
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://wesbos.com/courses"&gt;Wes Bos' courses&lt;/a&gt;
In general high quality video courses on various topics (many of them focusing on JS).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://leveluptutorials.com/"&gt;LevelUp Tutorials&lt;/a&gt;
Scott Tolinski's project, he has a great delivery in his video courses. His business model is slightly different from Wes'. You pay a monthly fee and get access to all his courses. Scott is prolific author whilst maintaining a good quality.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://frontendmasters.com/"&gt;Frontend Masters&lt;/a&gt;
Delivers what it says on the box – videos. I especially appretiated the courses made by Kyle Simpson.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sitepoint.com/premium/library/"&gt;Sitepoint Premium&lt;/a&gt;
A multitude of various video courses and books. I bought a lifetime membership when it still was called Learnable and it was one of the best investments into my self-education I made.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codecademy.com"&gt;Codecademy&lt;/a&gt;
One of the first interactive course platforms I stumbled upon around 2012(?). They're still great, even better.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learngitbranching.js.org/"&gt;Learn Git Branching&lt;/a&gt;
An interactive course that will let you try all the &lt;code&gt;git&lt;/code&gt; commands without the fear for breaking anything. It has a nice visual representation. I learned to navigate the commit tree thanks to this course.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webpack.academy/"&gt;Webpack Learning Academy&lt;/a&gt;
After these free video courses, Webpack started to make some sense.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://freecodecamp.org/"&gt;Free Code Camp&lt;/a&gt;
Interactive course platform and community. I really like this for its noble values – providing programming courses – and the quality is also pretty good.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cryptozombies.io/"&gt;Crypto Zombies&lt;/a&gt;
This is kind of funny, but I guess a good intro into blockchain programming.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Books
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/3735293-clean-code"&gt;Clean Code, &lt;em&gt;Robert C. Martin Series&lt;/em&gt;&lt;/a&gt;
This book showed me how to write code which is easier to understand. Since we started applying the principles outlined in this book on our team, the understanding of code in between developers became a breeze. Uses Java syntax for examples.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/getify/You-Dont-Know-JS"&gt;You Don't Know JS, &lt;em&gt;Kyle Simpson&lt;/em&gt;&lt;/a&gt;
I came across this series when I already had pretty solid knowledge of Javascript. Or at least so I thought! These books go into the inner workings of the JS engine and explain how it actually works. The first version was so good that I happilly supported Kyle in his effort to write and publish the second edition.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/26420.CSS"&gt;CSS: The Definitive Guide, &lt;em&gt;Eric A. Meyer&lt;/em&gt;&lt;/a&gt;
This book was hard to read as it's essentially a reference guide with explanations. BUT it was this book that gave me the understanding of how the CSS box model works.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.learninpublic.org/"&gt;The Coding Career Handbook, &lt;em&gt;Shawn @swyx Wang&lt;/em&gt;&lt;/a&gt;
Another great book. Swyx collected his tips based in a wide spectrum of knowledge from the IT industry.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://daveceddia.podia.com/pure-react-complete-package"&gt;Pure React, David Cedia&lt;/a&gt;
David taught me to &lt;em&gt;think React&lt;/em&gt;. This deffo helped me to understand how React works. I recommended this book to all my collegues and it really shows who read it and who did not.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sitepoint.com/premium/books/ecmascript-2015-a-sitepoint-anthology/"&gt;ECMAScript 2015: A SitePoint Anthology&lt;/a&gt;
A collection of articles about Javascript ES6.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sitepoint.com/premium/books/jump-start-git-2nd-edition"&gt;Jump Start Git, &lt;em&gt;Shaumik Daityari&lt;/em&gt;&lt;/a&gt;
A consise description of what Git is and how to use it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="//sitepoint.com/premium/books/professional-git"&gt;Professional Git&lt;/a&gt;
Quoting the classic: &lt;em&gt;"We gotta go deeper!"&lt;/em&gt; This is an indepth look into using Git.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Talks, presentations and podcasts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=1ffBJ4sVUb4&amp;amp;t=14s"&gt;Git for a four year old&lt;/a&gt;
The inner workings of &lt;code&gt;git&lt;/code&gt;. This is a must-watch!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ladybug.dev/"&gt;Ladybug Podcast&lt;/a&gt;
Emma Bostian, Sidney Buckner, Kelly Vaughn and Ali Spittel – four women in tech sharing their experiences. These seasoned web developers discuss hot technologies, advices for beginners and much more.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://syntax.fm/"&gt;Syntax.fm&lt;/a&gt;
Wes Bos and Scott Tolinski team up to create this gem of a podcast. They present the newest trends, their personal expertise based on years of teaching web development and invite interesting guests to discuss their products and fields of expertise.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gutenbergtimes.com/podcast/"&gt;Gutenberg Times&lt;/a&gt;
The official Wordpress podcast dedicated to spreading the Word of our Lord Gutenberg 🤣 I get to know about the latest addition to the Gutenberg plugin as well as what got integrated into the WP core and what direction is the developement going.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://swyx.transistor.fm/"&gt;The Swyx Mixtape&lt;/a&gt;
Swyx's short interviews with interesting developers.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Cover image: &lt;a href="https://www.emmaplunkett.art/artwork/cafe-con-leche/"&gt;Café con Leche by Emma Plunkett&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>learning</category>
      <category>developer</category>
      <category>resources</category>
    </item>
    <item>
      <title>A NodeJS application calling a 3rd-party API</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Fri, 25 Mar 2022 21:16:29 +0000</pubDate>
      <link>https://dev.to/crs1138/a-nodejs-application-calling-a-3rd-party-api-2p31</link>
      <guid>https://dev.to/crs1138/a-nodejs-application-calling-a-3rd-party-api-2p31</guid>
      <description>&lt;p&gt;This article aims to sum up my learning from developing and deploying my first NodeJS application. The idea is that I need to access somebody else's server to receive information. The communication with the server is only possible through a security token which I don't want to reveal to the visitor of my front-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can see the app in action at my &lt;a href="https://node-api-call.herokuapp.com/" rel="noopener noreferrer"&gt;node-api-call.heroku.com&lt;/a&gt;. It should work as long as &lt;a href="https://rapidapi.com/sameer.kumar/api/aztro" rel="noopener noreferrer"&gt;Aztro – the 3rd-party API&lt;/a&gt; is still functioning. Either way, you're welcome to have a look at my code at my &lt;a href="https://github.com/crs1138/node-api-call" rel="noopener noreferrer"&gt;Github repo – node-api-call&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wanted to achieve
&lt;/h2&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1647780008727%2FWsnrG29vB.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1647780008727%2FWsnrG29vB.png" alt="communication-diagram.drawio-A5.png"&gt;&lt;/a&gt;&lt;/p&gt;
Fig. 1 – Communication diagram between visitor's browser our server and the 3rd-party API



&lt;p&gt;The diagram above can be read as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visitor of the webpage submits the input data and the browser sends a request to my server-side application running on the Heroku platform&lt;/li&gt;
&lt;li&gt;My application decides the browser's request requires sending a request to the 3rd-party API, attaching the API's authentication key.&lt;/li&gt;
&lt;li&gt;The 3rd-party API answers the request with a response.&lt;/li&gt;
&lt;li&gt;My app processes the response and answers to the browser.&lt;/li&gt;
&lt;li&gt;The browser receives the answer and processes it. &lt;/li&gt;
&lt;li&gt;If the answer is correct the browser will display the horoscope for the requested day and Zodiac sign.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/download/package-manager/" rel="noopener noreferrer"&gt;Install NodeJS&lt;/a&gt; locally; I use the NVM (Node Version Manager). I'm using the Node@v16&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt;Heroku account&lt;/a&gt; – optional, you can use Vercel or any other Node platform out there&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://rapidapi.com/hub" rel="noopener noreferrer"&gt;RapidAPI account&lt;/a&gt; and register an app based on the &lt;a href="https://rapidapi.com/sameer.kumar/api/aztro/" rel="noopener noreferrer"&gt;Aztro API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I presume you have a basic knowledge of Javascript, HTML, CSS and know how JS works in the browser.&lt;/li&gt;
&lt;li&gt;I expect you to know how to use Git and to have your own Github account and know how to interact with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What have I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://axios-http.com/" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; – a promise based HTTP client for NodeJS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;ExpressJS&lt;/a&gt; – a NodeJS web application, this is the server and a router, processing visitors' requests and providing their browsers with responses.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/motdotla/dotenv#readme" rel="noopener noreferrer"&gt;Dotenv&lt;/a&gt; – a module that loads environment variables into &lt;code&gt;process.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pillarjs/hbs#readme" rel="noopener noreferrer"&gt;hbs&lt;/a&gt; – ExpressJS view engine for &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;handlebars.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://heroku.com" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; – a platform that let me publish my NodeJS application to the world.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Axios – fetch the data from the remote API
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;See the &lt;a href="https://github.com/crs1138/node-api-call/tree/axios" rel="noopener noreferrer"&gt;axios branch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My first goal was to get my NodeJS to communicate with the Aztro API.&lt;/p&gt;

&lt;p&gt;Even though NodeJS has announced the arrival of Fetch API to its v17.5 as an experimental feature, the popular way of fetching HTTP requests is undoubtedly using Axios.&lt;/p&gt;

&lt;p&gt;To achieve this goal, I created a new Git repo, linked it with Github.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Then I initialised a new Node Package Module with the default values  &lt;code&gt;npm init --yes&lt;/code&gt;. You can always edit them later on.&lt;/li&gt;
&lt;li&gt;Installed the Axios and Dotenv packages &lt;code&gt;npm install axios dotenv&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Created the basic file structure.&lt;/li&gt;
&lt;li&gt;Added the &lt;code&gt;.env&lt;/code&gt; file containing the &lt;code&gt;RAPIDAPI_KEY&lt;/code&gt;. I also added the &lt;code&gt;.env&lt;/code&gt; file to the &lt;code&gt;.gitignore&lt;/code&gt; so the token is not made public. Use your own instead.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2d7d&lt;span class="k"&gt;***&lt;/span&gt;...&lt;span class="k"&gt;***&lt;/span&gt;8037
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point my app had the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── .env
├── .gitignore
├── package-lock.json
├── package.json
└── src
    ├── app.js
    └── astrology.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;src/app.js&lt;/code&gt; contains the code necessary for importing the &lt;code&gt;src/astrology.js&lt;/code&gt;, creating and calling the asynchronous call of the call to the Aztro API.&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;// src/app.js&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&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;HoroscopeAPI&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;./astrology&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;asyncApiCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;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;HoroscopeAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHoroscope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;today&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;asyncApiCall&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 the app so far, you can launch &lt;code&gt;node src/app.js&lt;/code&gt;. Note that the initiation of the &lt;code&gt;require(dotenv).config()&lt;/code&gt; needs to come before requiring the &lt;code&gt;src/astrology&lt;/code&gt; module otherwise the value of &lt;code&gt;RAPIDAPI_KEY&lt;/code&gt; won't be available to the code responsible for communicating with the API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
Dotenv loads the environment variables that are stored in the &lt;code&gt;.env&lt;/code&gt; file in the root of the project. You might have noticed that this file is not included in the repository. That's right. I configured Git to ignore that file and that is where my project-specific secrets are kept. In this case, the secret is the &lt;code&gt;RAPIDAPI_KEY&lt;/code&gt;. I subsequently set the value of this variable on the server hosting the staging and production versions of the web app.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/astrology.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://sameer-kumar-aztro-v1.p.rapidapi.com/`&lt;/span&gt;

&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;today&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sameer-kumar-aztro-v1.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RAPIDAPI_KEY&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;getHoroscope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&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;Notice the hardcoded values for the API call where&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;today&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;If everything works as it's supposed to then you should see something like this in your terminal.&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="nl"&gt;date_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;May 21 - Jun 21&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;current_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;March 20, 2022&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You're an open book these days and anyone who stops to read your pages better be prepared for the unbridled truth. Some will admire your frankness, while others might be shocked.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;compatibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Taurus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Truthful&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Navy Blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lucky_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;54&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lucky_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2pm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 – The application serving UI to the visitor and processing their requests
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;See the &lt;a href="https://github.com/crs1138/node-api-call/tree/basic-app" rel="noopener noreferrer"&gt;basic-app branch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, I have the &lt;code&gt;axios()&lt;/code&gt; part working. It's time to move on to the next step – creating an app that processes requests that come from visitors' browsers and provides them with a user interface.&lt;/p&gt;

&lt;p&gt;I've refactored the code, giving the &lt;code&gt;src/app.js&lt;/code&gt; more of the prominent encompassing role whilst moving the other code around to &lt;code&gt;src/utils/astrology.js&lt;/code&gt; and for Handlebars based templates into the &lt;code&gt;src/templates/&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;You can see the refactored structure in the following diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── .env
├── .gitignore
├── package-lock.json
├── package.json
├── public
│   ├── css
│   │   └── styles.css
│   └── js
│       └── app.js
└── src
    ├── app.js
    ├── templates
    │   ├── partials
    │   │   └── header.hbs
    │   └── views
    │       ├── 404.hbs
    │       └── index.hbs
    └── utils
        └── astrology.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ExpressJS
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've decided to use ExpressJS to help me with responding to the requests coming from the visitor's browser as well as launching the Axios requests to the Aztro API. It is pretty much my first deployed interaction with ExpressJS, so I am no expert, but it seems reasonably straightforward and well documented. I guess that's one of the reasons why it's so popular.&lt;/p&gt;

&lt;p&gt;I have implemented the following responses to what the visitor might try to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A general request to the homepage: &lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;POST&lt;/code&gt; request submitted by the form on the generic app's page &lt;code&gt;/horoscope&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Anything else – &lt;code&gt;*&lt;/code&gt; – should produce a 404 error&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  The application setup
&lt;/h4&gt;

&lt;p&gt;First, I instantiate the ExpressJS app and tell it to parse incoming requests as JSON.&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;// src/app.js&lt;/span&gt;
&lt;span class="c1"&gt;// Create the app&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Add JSON parsing middleware&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Handling the Handlebars templates
&lt;/h4&gt;

&lt;p&gt;Next, I set up the app to handle serving the responses to the browser using the Handlebars (hbs) view engine.&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;// src/app.js&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// Define paths for the HBS config&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./templates/views&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;partialsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./templates/partials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hbs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewsPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;hbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerPartials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;partialsPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Setup static directory to serve&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set the default &lt;code&gt;view engine&lt;/code&gt; to &lt;code&gt;'hbs'&lt;/code&gt;. This will enable me to render the &lt;code&gt;.hbs&lt;/code&gt; files when the &lt;code&gt;response.render()&lt;/code&gt; function is called using Handlebars.&lt;/p&gt;

&lt;p&gt;I tell the ExpressJS app where to look for the Handlebars views. There are only two views for this app – &lt;code&gt;index.hbs&lt;/code&gt; (for the rendering of the app) and &lt;code&gt;404.hbs&lt;/code&gt; (this is used to render any other route as a 404 error page). I pass the path to these views to the &lt;code&gt;views&lt;/code&gt; property of the ExpressJS app.&lt;/p&gt;

&lt;p&gt;Next, I let the &lt;code&gt;hbs&lt;/code&gt; know where to look for all the template partials, passing their path to the &lt;code&gt;hbs.registerPartials()&lt;/code&gt; function. In our case, the only partial so far is the &lt;code&gt;header.hbs&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  App's routing
&lt;/h4&gt;

&lt;p&gt;The first route is for the index page that will be shown to visitors when they access the app. The &lt;code&gt;app.get(path, callback)&lt;/code&gt; function tells our app when a GET request comes to react with the callback middleware. For my purpose, the response is to render the &lt;code&gt;index&lt;/code&gt; view with the &lt;code&gt;title&lt;/code&gt; variable being set to Horoscope.&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;// src/app.js&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// Create base URL route "/" and render index view&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Horoscope&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;The second route is the one that I shall use for processing the API request. The route is &lt;code&gt;/horoscope&lt;/code&gt; and it matches the request's route defined in the &lt;code&gt;fetch&lt;/code&gt; call from the frontend JS located in &lt;code&gt;/public/js/app.js&lt;/code&gt;. This request carries data in the format of JSON made out of an object with two properties: &lt;code&gt;sign&lt;/code&gt; and &lt;code&gt;day&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="c1"&gt;// src/app.js&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// Response to the POST request made by submitting the app's form&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/horoscope&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;day&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide all details&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;horoscope&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;HoroscopeAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchHoroscope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;horoscope&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong on the server side&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;Destructuring the &lt;code&gt;sign&lt;/code&gt; and &lt;code&gt;day&lt;/code&gt; properties from &lt;code&gt;request.body&lt;/code&gt;, making sure they are defined, I try to call the asynchronous function that I developed in the first step.&lt;/p&gt;

&lt;p&gt;I moved the code into its own partial &lt;code&gt;/src/utils/astrology.js&lt;/code&gt; – very handy if there was more than one method to interact with the Aztro API. &lt;/p&gt;

&lt;p&gt;Have a look for yourself as I adapted the structure a little bit to make it work as a self-contained JS module providing the &lt;code&gt;HoroscopeAPI.fetchHoroscope(sign, day)&lt;/code&gt; method. This method creates a closure over the Axios call making sure we can pass the &lt;code&gt;sign&lt;/code&gt; and &lt;code&gt;day&lt;/code&gt; variables and sets the remaining necessary options (&lt;code&gt;url&lt;/code&gt;, &lt;code&gt;method&lt;/code&gt;, request's &lt;code&gt;headers&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="c1"&gt;// /src/utils/astrology.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://sameer-kumar-aztro-v1.p.rapidapi.com/`&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fetchHoroscope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;day&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sameer-kumar-aztro-v1.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RAPIDAPI_KEY&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="nf"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And last but not least is the route for any other requests. This is to respond to such requests with a 404 error page.&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;// Catch all route, renders 404 page&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;404&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;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the only thing left to do is set the web server's port and let the server listen for incoming requests.&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;// Initialize application port&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&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;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is up on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shortly about the frontend (read browser) Javascript
&lt;/h2&gt;

&lt;p&gt;All the static code intended for the use in visitor's browser can be found in the &lt;code&gt;/public/css&lt;/code&gt; and &lt;code&gt;/public/js&lt;/code&gt; folders. I presume that is nothing too new for you and thus I shall focus only on the part that actually sends requests to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/horoscope&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
            &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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;description&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request is sent the &lt;code&gt;/horoscope&lt;/code&gt; path as mentioned above. The body of the request is a JS object converted to the textual representation of JSON. I tell the server that the body is in that format by providing the &lt;code&gt;'Content-Type': 'application/json'&lt;/code&gt; header. The &lt;code&gt;fetch()&lt;/code&gt; function returns a promise with a response and when this resolves the &lt;code&gt;response.json()&lt;/code&gt; returns a promise that resolves into an object with the &lt;code&gt;data&lt;/code&gt; property. That explains the need for the chain of &lt;code&gt;then()&lt;/code&gt; methods. If you're not sure how this works, I recommend you use &lt;code&gt;console.log()&lt;/code&gt; to reflect on the sequence of the two promises and what results they provide.&lt;/p&gt;

&lt;p&gt;Please notice, as soon as the form is submitted, I display the &lt;code&gt;'Loading…'&lt;/code&gt; text that is then replaced with the response from the server. This can be either the results received from Aztro API or a generic error message in case of any problem the server might experience.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover image &lt;a href="https://www.emmaplunkett.art/artwork/fishy-fish/" rel="noopener noreferrer"&gt;Fishy Fish by Emma Plunkett Art&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>codenewbie</category>
      <category>learning</category>
      <category>express</category>
    </item>
    <item>
      <title>Gutenberg cheatsheet – Block's `supports` property</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Mon, 21 Mar 2022 17:12:23 +0000</pubDate>
      <link>https://dev.to/crs1138/gutenberg-cheatsheet-blocks-supports-property-23gk</link>
      <guid>https://dev.to/crs1138/gutenberg-cheatsheet-blocks-supports-property-23gk</guid>
      <description>&lt;p&gt;Gutenberg's documentation has come a long way and it's getting better and better. However, now and then I find myself missing a quick overview of the properties. Thus the idea of a cheatsheet…&lt;/p&gt;

&lt;p&gt;Here's a short overview of the individual properties of the &lt;code&gt;supports&lt;/code&gt; object for details refer to &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/"&gt;developer's documentation&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#anchor"&gt;&lt;code&gt;anchor&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;false&lt;/code&gt;.
It doesn't work with dynamic blocks (yet).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#align"&gt;&lt;code&gt;align&lt;/code&gt;&lt;/a&gt; - boolean/array: &lt;code&gt;false&lt;/code&gt;
No default alignment is assigned. If you need to set a default value, declare the &lt;code&gt;align&lt;/code&gt; attribute with its default.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#alignwide"&gt;&lt;code&gt;alignWide&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;false&lt;/code&gt;
Disable &lt;a href="https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/#wide-alignment"&gt;wide alignment&lt;/a&gt; for a single block.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#classname"&gt;&lt;code&gt;className&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
&lt;strong&gt;By default, the class &lt;code&gt;.wp-block-your-block-name&lt;/code&gt; is added to the root element of your saved markup.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#color"&gt;&lt;code&gt;color&lt;/code&gt;&lt;/a&gt; - Object: &lt;code&gt;null&lt;/code&gt;
This value signals that a block supports some of the properties related to colour. When it does, the block editor will show UI controls for the user to set their values.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#customclassname"&gt;&lt;code&gt;customClassName&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
Controls whether the field for custom class name is displayed in the panel inspector.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#defaultstylepicker"&gt;&lt;code&gt;defaultStylePicker&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
When the style picker is shown, the user can set a default style for a block type based on the block’s currently active style.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#html"&gt;&lt;code&gt;html&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
By default, a block’s markup can be edited individually.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#inserter"&gt;&lt;code&gt;inserter&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
To hide a block so it can only be inserted programmatically, set the &lt;code&gt;inserter: false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#multiple"&gt;&lt;code&gt;multiple&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
If you want a block to be inserted into each post only once.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#reusable"&gt;&lt;code&gt;reusable&lt;/code&gt;&lt;/a&gt; - boolean: &lt;code&gt;true&lt;/code&gt;
You can prevent a block from being converted into a reusable block.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#spacing"&gt;&lt;code&gt;spacing&lt;/code&gt;&lt;/a&gt; - Object: &lt;code&gt;null&lt;/code&gt;
You can enable some of the CSS style properties related to spacing. Good for margin and padding overrides, if &lt;a href="https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-support/#cover-block-padding"&gt;the theme declares support&lt;/a&gt;.
Subproperties:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;margin&lt;/code&gt; - boolean/array: &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;padding&lt;/code&gt; - boolean/array: &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/#typography"&gt;&lt;code&gt;typography&lt;/code&gt;&lt;/a&gt; - Object: &lt;code&gt;null&lt;/code&gt;
When enabled, the block editor will show a typography UI.
Subproperties:

&lt;ul&gt;
&lt;li&gt;fontSize - boolean: &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;lineHeight - boolean: &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The values of the attributes added by the &lt;code&gt;supports&lt;/code&gt; object will be added to the object returned from the &lt;code&gt;useBlockProps()&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;Edit&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Edit&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;blockProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useBlockProps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;blockProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;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;&lt;code&gt;Save&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Save&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;blockProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useBlockProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;blockProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;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 dynamic and server-side rendered blocks these extra attributes can get rendered in the &lt;code&gt;render_callback&lt;/code&gt; in PHP using the function &lt;code&gt;get_block_wrapper_attributes()&lt;/code&gt;. It returns a string containing all the generated properties and needs to get output in the opening tag of the wrapping block element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;render_block&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$wrapper_attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_block_wrapper_attributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'&amp;lt;div %1$s&amp;gt;%2$s&amp;lt;/div&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;$wrapper_attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'Lorem ipsum'&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;Cover image by &lt;a href="https://www.emmaplunkett.art/artwork/missing-peace/"&gt;Emma Plunkett – The Missing Peace&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>gutenberg</category>
      <category>cheatsheet</category>
    </item>
    <item>
      <title>Gutenberg – Block Deprecation</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Mon, 14 Mar 2022 21:42:19 +0000</pubDate>
      <link>https://dev.to/crs1138/gutenberg-block-deprecation-418e</link>
      <guid>https://dev.to/crs1138/gutenberg-block-deprecation-418e</guid>
      <description>&lt;p&gt;When you start writing your own Gutenberg block, it's just a matter of time that you will write a new version of a block. You might be wandering what that means for your existing content. What will happen when you change the structure returned by the &lt;code&gt;save()&lt;/code&gt; function of the block? The devs behind the Gutenberg project thought about this too. There is a mechanism that will mark the affected blocks, saying: "This block contains unexpected or invalid content." and if you have a peek in the JS console you might get a heart attack – so many errors! 😀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IY0ZlvCc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1647103674609/UqjrWWFGs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IY0ZlvCc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1647103674609/UqjrWWFGs.png" alt="00b93a42e5c04dc11af23f121c07ee2f.png" width="448" height="225"&gt;&lt;/a&gt;&lt;/p&gt;
An example of the error of a deprecated block.



&lt;p&gt;Personally, I'd rather skip over the most painful possibility of solving this – revision of all your content where the upgraded block appears and attempting the block recovery. This leaves you with two remaining strategies how to address this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new block with a different name and start using that.&lt;/li&gt;
&lt;li&gt;Define a deprecate version of the blocks and allow users to edit them using their new version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does the deprecation actually work
&lt;/h2&gt;

&lt;p&gt;I find it helpful to understand the order of execution what Wordpress does when the editor notices that the DOM structure produced by the new version of the blocks &lt;code&gt;save()&lt;/code&gt; function doesn't match the one saved in the content. Here is what happens on the &lt;code&gt;save&lt;/code&gt; side (pun intended) when editor comes across a block:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read all the attributes and run the block's &lt;code&gt;save()&lt;/code&gt; function and compare its result to the block's content saved on the page. Do they match? No?&lt;/li&gt;
&lt;li&gt;Look for the deprecation array and iterate the attributes and &lt;code&gt;save&lt;/code&gt; functions stored in these. The important gotcha here is that the array is not sequence list. The results of one deprecated version are NOT passed to the next one. Each deprecated version is evaluated independently in the order left to right as they are listed in the array. If the results of the &lt;code&gt;save&lt;/code&gt; function do not satisfy the requirements, the editor moves onto the next one. If none of them can produce a satisfactory result, the &lt;em&gt;Attempt Block Recovery&lt;/em&gt; overlay is displayed and errors are printed in the JS console.&lt;/li&gt;
&lt;li&gt;When a fit is found, editor looks for &lt;code&gt;migrate&lt;/code&gt; function. This is especially handy when the &lt;code&gt;&amp;lt;Innerblocks /&amp;gt;&lt;/code&gt; are involved. This function lets the developer to handle changes to the attributes and/or to create a new structure of the inner blocks then pass these to the new (current) version of the block's &lt;code&gt;save&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;The values of the attributes and the template of inner blocks are passed to the new &lt;code&gt;save&lt;/code&gt; function and a new content is saved when the page is updated.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Structure of the deprecated version of a block
&lt;/h2&gt;

&lt;p&gt;Every single deprecated version of the block is an object. This object has the following structure and you don't have to provide all properties.&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;v5_0_0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// the structure of attributes as they were defined in the &lt;/span&gt;
        &lt;span class="c1"&gt;// deprecated version&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// the supports definition of the deprecated block&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;innerBlocks&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// just recreate the DOM structure with content provided by &lt;/span&gt;
        &lt;span class="c1"&gt;// the block attributes of this version of the block&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;innerBlocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// this function receives old attributes and inner blocks and&lt;/span&gt;
        &lt;span class="c1"&gt;// returns either attributes or an array [attributes, innerBlocks] &lt;/span&gt;
        &lt;span class="c1"&gt;// to be used in the current block version&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;isEligible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If the function returns true, this deprecated version will be used.&lt;/span&gt;
        &lt;span class="c1"&gt;// Use this if the block is technically valid but you need to update &lt;/span&gt;
        &lt;span class="c1"&gt;// its attributes&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;
  
  
  &lt;code&gt;attributes&lt;/code&gt; object
&lt;/h3&gt;

&lt;p&gt;The attributes of the deprecated form of the block. The amount of attributes can change quite dramatically. When we refactored our Testimonial block the new version was using a template made out of the &lt;code&gt;core/paragraph&lt;/code&gt; blocks as the inner blocks of the &lt;code&gt;my-plugin/testimonial&lt;/code&gt; block. The previous version of Testimonial was relying on the RichText component and storing their values in attributes &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;position&lt;/code&gt; and &lt;code&gt;comment&lt;/code&gt;. Each inner block handles its own attributes so suddenly there was no need for our block to handle these.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;excerpt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;deprecated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;block.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;my-plugin/testimonial&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".my-plugin__position"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".my-plugin__comment"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"faceOption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new version was left with just the attribute storing the value defining the image of the author of the testimonial.&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;// excerpt from the block.json after the refactor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;attributes&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;faceOption&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;type&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;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;supports&lt;/code&gt; object
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;supports&lt;/code&gt; (object) definition for the deprecated form of the block. The property &lt;code&gt;supports&lt;/code&gt; is one to pay a closer attention. This is not inherited from the new version. If it is not defined it has the default values as described at &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/"&gt;WP dev's documentation&lt;/a&gt;. Not being aware of this can lead to a hair loss due to unexpected behaviour. My deprecation &lt;code&gt;save()&lt;/code&gt; function was getting the block's class name even when I explicitly took out any dynamic code for the classes.&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;// Let's call this block my-plugin/box&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deprecated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="nx"&gt;save&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="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was producing the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wp-block-my-plugin-testimonial"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Lorem ipsum&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Needless to say, it took me a while to arrive to such simplified use-case. Imagine how perplexed I was when I explicitly set the class to be an empty string and the block's default class name still appeared. And this was the only thing that would make this deprecation to be skipped. The solution is in disabling the blocks class name in the deprecations &lt;code&gt;supports&lt;/code&gt; property.&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;deprecated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="nx"&gt;save&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="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;supports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;className&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="p"&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;save( props )&lt;/code&gt; function
&lt;/h3&gt;

&lt;p&gt;The save implementation of the deprecated form of the block. The editor parser uses this function to compare its result with the stored content and in case it matches it will attempt to use this deprecated version of the block. It will then look into the attributes of that version and will try to use or migrate these with the new version of the block. Other than that it works the same as the usual &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save"&gt;block's &lt;code&gt;save()&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;migrate( attributes, innerBlocks )&lt;/code&gt; function (optional)
&lt;/h3&gt;

&lt;p&gt;As I mentioned above this function will run only if the deprecated version of the &lt;code&gt;save()&lt;/code&gt; function produces a valid block. You need to make sure that all the deprecated version have their own &lt;code&gt;migrate()&lt;/code&gt; if it is relevant for the upgrade of the block.&lt;/p&gt;

&lt;p&gt;In the case of our testimonial block the &lt;code&gt;migrate()&lt;/code&gt; function takes the old attributes and the old inner blocks and returns values for the new version of attributes and inner blocks.&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;migrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;oldInnerBlocks&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;cleanedUpInnerBlocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oldInnerBlocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;innerBlock&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;attributes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;innerBlock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// remove all the old inner block's classes and replace them with my class name&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-plugin__name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;innerBlock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newAttributes&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;newInnerBlocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;cleanedUpInnerBlocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the previous version happened to use an innerBlock for the testimonial author's name&lt;/span&gt;
        &lt;span class="nx"&gt;createBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;core/paragraph&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;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-plugin__position&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;oldAttributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="nx"&gt;createBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;core/paragraph&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;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-plugin__comment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;oldAttributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newInnerBlocks&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;
  
  
  &lt;code&gt;isEligible( attributes, innerBlocks )&lt;/code&gt; function (optional)
&lt;/h3&gt;

&lt;p&gt;This function will be called on every valid instance of the block. It can be useful, if you need to use a deprecation even though the result of the &lt;code&gt;save()&lt;/code&gt; function matches the saved content. Think, for example, of a change in the attributes structure whilst the output returned by the &lt;code&gt;save()&lt;/code&gt; functions remains the same.&lt;/p&gt;

&lt;p&gt;Also, because it is called on every single valid (new) instance of the block, it is better to optimise for the false condition by performing a naive, inaccurate pass at inner blocks. Only when the fast pass is considered eligible is the more accurate, durable and &lt;strong&gt;slower&lt;/strong&gt; condition evaluated.&lt;/p&gt;

&lt;p&gt;A good and commented example of using the &lt;code&gt;isEligible()&lt;/code&gt; function is the &lt;a href="https://github.com/WordPress/gutenberg/blob/1eaad3cbad327e9fad5d8eef93c46aa773b7080b/packages/block-library/src/columns/deprecated.js#L127"&gt;deprecation of the core/columns block&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image: &lt;a href="https://emmaplunkett.art"&gt;Moorish Archways by Emma Plunkett Art&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>learning</category>
      <category>gutenberg</category>
    </item>
    <item>
      <title>Create A File Structure For React Components Using Xargs Command</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Tue, 23 Mar 2021 21:33:40 +0000</pubDate>
      <link>https://dev.to/crs1138/create-a-file-structure-for-react-components-using-xargs-command-4i4b</link>
      <guid>https://dev.to/crs1138/create-a-file-structure-for-react-components-using-xargs-command-4i4b</guid>
      <description>&lt;p&gt;When I need to quickly knock up a bunch of React components, I like to use my command line. This is how I create a directory structure, where each element has its own folder. Let's say I want something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-web-app/src
├── App
│   └── App.jsx
├── CtaButton
│   └── CtaButton.jsx
├── CurrencyBar
│   └── CurrencyBar.jsx
├── Discount
│   └── Discount.jsx
├── DomainForm
│   └── DomainForm.jsx
├── DomainInput
│   └── DomainInput.jsx
├── Header
│   └── Header.jsx
├── RadioButton
│   └── RadioButton.jsx
└── Results
    └── Results.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd go to the &lt;code&gt;src&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-web-app/src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create all folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;md App CtaButton CurrencyBar Discount DomainForm DomainInput Header RadioButton Results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run the following command to create all the JSX files within their folders, keeping the same name as the folders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d | xargs &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; | xargs &lt;span class="nt"&gt;-I_&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;_/_.jsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cover image: Variegated Aerialist, Emma Plunkett Art © 2019&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>zsh</category>
      <category>cli</category>
    </item>
    <item>
      <title>How do you use the `&lt;LinkControl /&gt;` component in Gutenberg development?</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Fri, 24 Jul 2020 06:54:52 +0000</pubDate>
      <link>https://dev.to/crs1138/how-do-you-use-the-linkcontrol-component-in-gutenberg-development-9p5</link>
      <guid>https://dev.to/crs1138/how-do-you-use-the-linkcontrol-component-in-gutenberg-development-9p5</guid>
      <description>&lt;p&gt;I'm trying to develop a custom CTA button component that will be used in all of our custom Gutenberg blocks. I'd like the authors to be able to click on the button in the editor and change the button text, URL (with option to search the existing posts/pages/CPTs/…) and set the target and rel attributes. I've been playing around with &lt;code&gt;URLInputButton&lt;/code&gt;, &lt;code&gt;URLPopover&lt;/code&gt; and eventually stumbled upon &lt;a href="https://github.com/WordPress/gutenberg/tree/master/packages/block-editor/src/components/link-control"&gt;&lt;code&gt;LinkControl&lt;/code&gt;&lt;/a&gt;, but I just can't get it to work at all. The documentation doesn't provide any example of its use either. Does anybody here knows?&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;// A simplified example showing what I've been trying so far…&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;props&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;btnText&lt;/span&gt;&lt;span class="p"&gt;,&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;setAttributes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// &amp;lt;ButtonGroup className={`flex cta`}&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;//     {/* &amp;lt;Button url={`#url`}&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;//     &amp;lt;/Button&amp;gt; */}&lt;/span&gt;
        &lt;span class="c1"&gt;//     &amp;lt;Button url={url} &amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;//     &amp;lt;PlainText&lt;/span&gt;
        &lt;span class="c1"&gt;//         placeholder={ __(`Calling to act`, cvslug) }&lt;/span&gt;
        &lt;span class="c1"&gt;//         value={btnText || `Click`}&lt;/span&gt;
        &lt;span class="c1"&gt;//         onChange={ (newText) =&amp;gt; setAttributes({ btnText: newText }) }&lt;/span&gt;
        &lt;span class="c1"&gt;//     /&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;//     &amp;lt;/Button&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;//     {/* &amp;lt;URLInputButton&lt;/span&gt;
        &lt;span class="c1"&gt;//         url={ url }&lt;/span&gt;
        &lt;span class="c1"&gt;//         onChange={ (url, post) =&amp;gt; setAttributes( { url, btnText: (post &amp;amp;&amp;amp; post.title) || `Click here` } ) }&lt;/span&gt;
        &lt;span class="c1"&gt;//     /&amp;gt; */}&lt;/span&gt;
        &lt;span class="c1"&gt;// &amp;lt;/ButtonGroup&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LinkControl&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;===&lt;br&gt;
&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@breakyourboundaries4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Matt Collamer&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/help?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>gutenberg</category>
      <category>help</category>
    </item>
    <item>
      <title>My story as a web developer – How I landed my job</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Fri, 19 Jun 2020 07:15:17 +0000</pubDate>
      <link>https://dev.to/crs1138/my-story-as-a-web-developer-how-i-landed-my-job-406e</link>
      <guid>https://dev.to/crs1138/my-story-as-a-web-developer-how-i-landed-my-job-406e</guid>
      <description>&lt;p&gt;This is the third part of my story about my journey toward becoming a web developer. Part one covers my first timid steps and part two is about my experience as a freelancer.&lt;/p&gt;

&lt;p&gt;I felt my learning accelerate when I eventually started working with various other developers and individuals, as well as small teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  I need to find some work
&lt;/h2&gt;

&lt;p&gt;Working with other developers positively reflected on my skills because I was being exposed to more complicated problems. However, my growth as a developer came at a price - I didn't have time to put into being an entrepreneur. If other people were in charge of the business part, I could focus on coding. If I'm completely honest with myself, I still prefer it that way. I do like my clients and I've always done my best to maintain a friendly yet professional relationship with them. My problem was that I found all the business and admin side of running things very distracting from what I am actually really interested in doing - programming. All the socializing, listening to their ideas of how they want to run their online business, trying to make sense of how they could achieve their goals, suggesting solutions, contracts, etc… I found that all quite boring.&lt;/p&gt;

&lt;p&gt;During the second half of the summer of 2019, my co-worker and (much as she hates that word) boss (in one person), Ippy, told me that some of her projects had been cancelled and Netuxo wouldn't have any work for me until late autumn or even the end of the year - I was unprepared. We'd just got back from a road trip around the EU, which cost us a bit more than we anticipated. The little savings we had left were quickly dwindling.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy2cylvb53fayk21xt9v0.jpg" 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%2Fi%2Fy2cylvb53fayk21xt9v0.jpg" alt="Fanad Head Lighthouse, Letterkenny, Ireland"&gt;&lt;/a&gt;&lt;/p&gt;
Are we moving to Letterkenny in the north of Ireland? Photo by &lt;a href="https://unsplash.com/@trevcole?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Trevor Cole&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;Letting go of my own business of 12 years was still a huge step to take, I had built it up from scratch alongside my wife but I’d known for a while that I needed to work as part of a bigger team, to stretch myself. Most of the work we'd been attracting just wasn’t challenging enough.&lt;/p&gt;

&lt;p&gt;I made sure Ippy knew I was looking for work elsewhere. I started looking at my options - the original idea was to work remotely. I turned to various online agencies such as &lt;a href="https://www.toptal.com/" rel="noopener noreferrer"&gt;Toptal&lt;/a&gt;, &lt;a href="https://www.honeypot.io/" rel="noopener noreferrer"&gt;Honeypot&lt;/a&gt; and &lt;a href="https://www.ciklum.com/" rel="noopener noreferrer"&gt;Ciklum&lt;/a&gt; but they didn't work out that well for me. I struggled with their hiring process, something that would later become clear, that it's a pattern in the IT industry… but more on that later.&lt;/p&gt;

&lt;p&gt;I started looking at jobs online. At first, within the Granada province but there weren't many listed. I'd registered with a bunch of online job portals such as &lt;a href="https://linkedin.com" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://www.glassdoor.es/" rel="noopener noreferrer"&gt;Glassdoor&lt;/a&gt; and the Spanish &lt;a href="https://www.tecnoempleo.com/" rel="noopener noreferrer"&gt;Tecnoempleo&lt;/a&gt;. I'd also sent out a few emails to my peers, asking them if they knew of any job openings and to keep their ear to the ground for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Curriculum Vitae
&lt;/h2&gt;

&lt;p&gt;The first step to being able to apply to all the various jobs I found advertised online was to make my CV. I was keeping my LinkedIn profile more or less up to date. I exported a PDF and sent it out to a selection of the jobs that seemed interesting to me - I didn't get a single response!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5spm6qx3m7q4tvvsubnb.jpg" 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%2Fi%2F5spm6qx3m7q4tvvsubnb.jpg" alt="Ross Castle, Killarney, Ireland"&gt;&lt;/a&gt;&lt;/p&gt;
Ireland is stunning but the weather can be very miserable. I just know I wouldn't last there, I need sunshine to be happy. Photo by &lt;a href="https://unsplash.com/@annejames?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Anne James&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;How could I make my CV better? I found the &lt;a href="https://www.reddit.com/r/resumes/" rel="noopener noreferrer"&gt;r/resume sub-reddit&lt;/a&gt; quite useful. One can post a resume there and people, including HR professionals, give you feedback - it's free! You may get contacted by a couple of professionals that watch that subreddit and use it to fill their funnel with potential leads but that’s ok, they are usually not too pushy and quite nice to talk to. Another little gem that came out of it, is the CV tool called &lt;a href="https://resumeworded.com/" rel="noopener noreferrer"&gt;Resume Worded&lt;/a&gt;. Their app lets you upload a CV in TXT or PDF, as well as import it from LinkedIn, it then runs an analysis of various aspects providing you with a detailed report of all the areas you should work on to improve your chances - I was only using the free version. After about 17 different attempts, I settled with a score of over 85%. I felt I was ready for my next round.&lt;/p&gt;

&lt;p&gt;I was still hoping to find a remote job because I didn't really want to leave the area. It was the first time in 15 years that we'd found a Spanish community that accepted us. I'd made real friends and felt really settled. However, I was getting increasingly more pragmatic about my whole situation because the opening positions were somewhat sporadic or offered really mediocre conditions. I broadened up my search radius to the whole of the EU except for the UK, as they were about to leave the Union.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Shameless plug and a spoiler at once:&lt;/em&gt; Funny enough my new job spins around CVs. What a coincidence! 🙂 I feel obliged to recommend you our product – &lt;a href="https://resumecoach.com" rel="noopener noreferrer"&gt;Resume Coach&lt;/a&gt; as I am convinced that it provides a great value for a reasonable price. Go and check it out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I sent out so many applications that I lost count. I must have been doing something right because the HR people noticed me and started scheduling interviews.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu7lr9jd9xpyyy51v0ore.jpg" 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%2Fi%2Fu7lr9jd9xpyyy51v0ore.jpg" alt="Atomium, Brussels, Belgium"&gt;&lt;/a&gt;&lt;/p&gt;
I was trying to imagine our life in Brussels. Photo by &lt;a href="https://unsplash.com/@les_photos_de_raph?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Raphaël Biscaldi&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;h2&gt;
  
  
  Job interviews
&lt;/h2&gt;

&lt;p&gt;If there's anything that can trigger my imposter syndrome, then it's the system that the 3rd party HR companies use to evaluate a candidate for a position. The vast majority of the technical interviews are done using a platform like &lt;a href="https://www.codility.com/" rel="noopener noreferrer"&gt;Codility&lt;/a&gt;. A good place for training for the technical interviews are also &lt;a href="https://www.codewars.com/" rel="noopener noreferrer"&gt;Code Wars&lt;/a&gt; or &lt;a href="https://www.hackerrank.com/" rel="noopener noreferrer"&gt;HackerRank&lt;/a&gt;. These are all quite straight forward, my problem is the way the HR teams prepare the interviews. Mostly, I was applying for frontend or full-stack positions but the technical interviews were not actually testing my ability in these fields of expertise. The tests were based on my knowledge of slightly advanced mathematics, something I knew but had last used when I was in the first or second year of my university studies. I hadn't needed a matrix or converging series since those days and all I remember is that they exist. I know where to find them if I ever need to learn them again, which is fine if I'm not in a timed technical interview exercise. Often, I found myself spending 90% of the allocated time on a quick math refresher and then just ran out of time for creating the algorithm and coding. You can just imagine how I felt after a couple of days filled with rejection after rejection, knowing full well that I wouldn't be against any of those problems (from the test) in a real-life job scenario.&lt;/p&gt;

&lt;p&gt;There were exceptions amongst the companies, some were a bit less interested in knowing whether they're hiring a good mathematician and did seem more interested in finding someone who is a good fit for the actual job vacancy they were trying to fill. Some of them, like for example Leadtech, would give me a well-prepared brief for the test exercise and a reasonable amount of time to complete the task at hand. They asked me to develop a frontend module with well-specified requirements, wireframes and graphic elements supplied by a graphic designer in InVision.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4hoqs1qs80khdtmby8dg.jpg" 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%2Fi%2F4hoqs1qs80khdtmby8dg.jpg" alt="Brussels, Belgium"&gt;&lt;/a&gt;&lt;/p&gt;
Starting from the zero in a country I can't speak their language as a freelancer? I think that would be a bit too much of a challenge in my 40s. Photo by &lt;a href="https://unsplash.com/@luizagiannelli?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Luiza Giannelli&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;After all these pointless boring mathematical quizzes, this kind of exercise was a pleasure to sink my teeth into. I made sure I over-delivered. I made a git repo, split the task into features and commented on my code, created a changelog, readme file and sent it back. In the third round of the interviews, whilst talking to the head of the frontend department and his superior, they asked me if I always give this level of attention to details. I said: “Absolutely! If the time allows me to do so." 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  Accepting the right offer
&lt;/h2&gt;

&lt;p&gt;Within a week or two I ended up with three solid offers. One in Letterkenny in Ireland, another in Brussels. Both of these well-paid gigs based on Drupal and related to projects of the European Union. The third one was Leadtech based in Barcelona.&lt;/p&gt;

&lt;p&gt;I couldn't contemplate living in the north of Ireland. My happiness is a function of sun exposure with a positive coefficient. The money they offered was good but deep inside, I knew I wouldn't last there for long.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fafw89mjlxfv8eej1xjef.jpg" 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%2Fi%2Fafw89mjlxfv8eej1xjef.jpg" alt="Barceloneta, Barcelona, Spain"&gt;&lt;/a&gt;&lt;/p&gt;
Should we go to the rainy Ireland? Or face the language barrier in Belgium? Or the sunny Barcelona? Photo by &lt;a href="https://unsplash.com/@benjagremler?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Benjamín Gremler&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;Brussels is slightly better weather-wise, the money, they were offering, was significantly superior. But there were buts… I would be working as a freelancer and the costs of living in Belgium is known to be enormous. I can't speak any French or Flemish – it was just a bit too much of a change.&lt;/p&gt;

&lt;p&gt;All in all the only viable option seemed to be Barcelona. After a week or so of weighing my options, I accepted their offer. I did make a rather embarrassing hiccup while accepting the offer though. The majority of the job ads do not advertise the salary, for obvious reasons. That's in case the candidate asks for less than they're ready to pay. So, it's sort of a negotiation game. The differences in salaries between different parts of Spain can be quite substantial. I hadn't kept any precise records of my talk with the HR people, so when I said, "Yes" to the offer, I accidentally asked for more money. There was an awkward silence on the other side of the phone and then the lady told me that she will have to speak about that with her colleague, the one who did the initial interview with me. Her colleague got back to me straight away and I could tell she wasn't very happy about my sudden extra demand. The difference was not too big and I'd already said, "No" to Letterkenny and Brussels - so I conceded.&lt;/p&gt;

&lt;p&gt;It all felt rather sudden. I mean, I knew this was a likely outcome, yet until I'd said, "Yes!" I wasn't actually believing it. So then, here I was going to Barcelona in a month or so. That's when it dawned on me - it wasn't just about me. My very supportive wife, with whom I consulted everything every step of the way, even before I started looking for a new job - I'd kept her updated on each and every possibility. One day she was daydreaming about the west coast of Ireland with me, then we were Googling different zones to live in Brussels and then we were musing on how Barcelona had changed over the years - she took it all rather calmly (luckily she’s always up for an adventure).&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flsimmlae3s5s83xq2l9i.jpg" 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%2Fi%2Flsimmlae3s5s83xq2l9i.jpg" alt="Bar in Barcelona"&gt;&lt;/a&gt;&lt;/p&gt;
Barcelona, they've got Czech beer on tap? I think this is settled! Photo by &lt;a href="https://unsplash.com/@alanaharris?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Alana Harris&lt;/a&gt; on &lt;a href="https://unsplash.com/collections/10595847/dev-journey---part3/6d7df70bc0c4ade49fd746356bd95265?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;It was happening – we were going to Barcelona! And in the midst of all this, yet another offer came in. A big international bank was looking for someone to join their team in Madrid. The team that was taking care of their Czech branch and they even said, "Yes" to me when I asked for more money. They were talking about a couple of business trips to Prague every year, so I could see my family. That was a tough call to make.&lt;/p&gt;

&lt;p&gt;I have been to Madrid a few times before. Once in the summer and twice in the winter. The winter visits were miserable. Coming from a mountainous region in Czech, I'd like to say that I've seen enough snow for the rest of my life. I'd already said, “Yes" to Leadtech, plus they were really nice to me and I’d enjoyed that very well prepared technical examination. My wife has a philosophy, she claims that we don't actually need to make any decisions in our lives, in due course we simply arrive at a point in time when things become obvious. In this case, the direction I should take – Barcelona, here I come!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Header image: &lt;a href="https://www.emmaplunkett.art/artwork/road-home/" rel="noopener noreferrer"&gt;The Road Home, Emma Plunkett Art © 2014&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>journey</category>
    </item>
    <item>
      <title>Jamstack Virtual Conference 2020</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Wed, 27 May 2020 17:52:59 +0000</pubDate>
      <link>https://dev.to/crs1138/jamstack-virtual-conference-2020-4233</link>
      <guid>https://dev.to/crs1138/jamstack-virtual-conference-2020-4233</guid>
      <description>&lt;h1&gt;
  
  
  What is your favourite talk so far?
&lt;/h1&gt;

&lt;p&gt;I really enjoyed the COVID-19 one and the one about emerging markets in Africa. What about you?&lt;/p&gt;

&lt;p&gt;Hop in – &lt;a href="https://hopin.to/events/jamstack-conf-virtual"&gt;https://hopin.to/events/jamstack-conf-virtual&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jamstackconf</category>
    </item>
    <item>
      <title>Curriculum Timeline – Stylus (CSS) Tutorial</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Sun, 10 May 2020 17:17:19 +0000</pubDate>
      <link>https://dev.to/crs1138/curriculum-timeline-stylus-css-tutorial-36nj</link>
      <guid>https://dev.to/crs1138/curriculum-timeline-stylus-css-tutorial-36nj</guid>
      <description>&lt;p&gt;Recently, I came across a need to display a list of artist's exhibitions in a sidebar on a webpage.  I wasn't quite satisfied with the styling of the presentation I had at hand at the time. That lead me to write a snippet of code you'll find in this tutorial. I wrote it in Stylus and HTML. Click on the &lt;em&gt;View Compiled&lt;/em&gt; button to see the compiled CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Curriculum Timeline – grab the code
&lt;/h2&gt;

&lt;p&gt;For those of you, who just wanna see it in action or just get the final code, here it is…&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/crs1138/embed/MWaGEdP?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Default settings and variables
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;bgColor&lt;/span&gt;   &lt;span class="o"&gt;?=&lt;/span&gt; &lt;span class="nn"&gt;#ffffff&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;txtColor&lt;/span&gt;  &lt;span class="o"&gt;?=&lt;/span&gt; &lt;span class="nn"&gt;#444444&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;linkColor&lt;/span&gt; &lt;span class="o"&gt;?=&lt;/span&gt; &lt;span class="nn"&gt;#ec008c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The basic structure
&lt;/h2&gt;

&lt;p&gt;AS you can see, in the code above, the actual timeline HTML is made of the following parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- The actual timeline HTML starts here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"timeline tm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- listing of the individual years --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__year"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__year"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__year"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__year"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- the actual vertical line representing the timeline --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__line"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty straight forward. The idea is an encapsulating block &lt;code&gt;.tm&lt;/code&gt; that has its property &lt;code&gt;position: relative&lt;/code&gt; to provide a point of reference for the vertical line &lt;code&gt;.tm__line&lt;/code&gt; which is positioned absolutely. The &lt;code&gt;.tm&lt;/code&gt; block also displays all the &lt;code&gt;.tm__year&lt;/code&gt; elements. I shall mention these in more detail later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tm&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;

  &lt;span class="cm"&gt;/* The vertical line symbolizing the timeline */
&lt;/span&gt;  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__line&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;
    &lt;span class="cm"&gt;/* defines the width of the vertical line */
&lt;/span&gt;    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-5rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;lighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$txtColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;80%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The structure of the individual year elements and their labels
&lt;/h2&gt;

&lt;p&gt;Each of the &lt;code&gt;.tm__year&lt;/code&gt; elements is made of an unordered list of the exhibitions that the artist participated in that year and a label for the particular year.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__year"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;2018&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__events"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__event"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__event"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__event"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; … &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The formatting of the &lt;code&gt;.tm__year&lt;/code&gt; makes each one of them &lt;code&gt;position: relative;&lt;/code&gt; to set a reference point for the &lt;code&gt;.tm__label&lt;/code&gt; element that is positioned absolutely. The label uses a set of &lt;code&gt;text-shadow&lt;/code&gt; properties to create the illusion of the text outline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;  &lt;span class="cm"&gt;/* Definition of the year elements */
&lt;/span&gt;  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__year&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;
    &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10rem&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt;
      &lt;span class="cm"&gt;/* making sure the first year sticks to the top */
&lt;/span&gt;      &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__label&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$txtColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$bgColor&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'PT Serif'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4rem&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-90deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;transform-origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="mi"&gt;.75rem&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16rem&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;
    &lt;span class="nl"&gt;text-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                  &lt;span class="nt"&gt;1px&lt;/span&gt; &lt;span class="nt"&gt;-1px&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt; &lt;span class="nt"&gt;rgba&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="nc"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                 &lt;span class="nt"&gt;-1px&lt;/span&gt;  &lt;span class="nt"&gt;1px&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt; &lt;span class="nt"&gt;rgba&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="nc"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; 
                  &lt;span class="nt"&gt;1px&lt;/span&gt;  &lt;span class="nt"&gt;1px&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt; &lt;span class="nt"&gt;rgba&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="nc"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The actual list of exhibitions
&lt;/h2&gt;

&lt;p&gt;There is nothing really special about the unordered list of exhibitions for each year. Each exhibition element consists of its heading and description.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__event"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tm__event__heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Ceramiq, Órgiva, Spain&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    A cyanotype exhibition.
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  …
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual styling of the list, its items and their headings&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__events&lt;/span&gt;
    &lt;span class="cm"&gt;/* Sets the right edge of the list in line with the vertical line */
&lt;/span&gt;    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;5rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="cm"&gt;/* Aligns the text of exhibitions to the right edge */
&lt;/span&gt;    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__event&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="nl"&gt;padding-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;
    &lt;span class="cm"&gt;/* set the reference point for the timeline bullet points */
&lt;/span&gt;    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;

    &lt;span class="err"&gt;…&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__heading&lt;/span&gt;
      &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;
      &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.2&lt;/span&gt;
      &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;
      &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$linkColor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The timeline bullet points
&lt;/h2&gt;

&lt;p&gt;Each event on the timeline is highlighted by a bullet point. These are made by the CSS &lt;code&gt;::after&lt;/code&gt; pseudo-element thus they don't require any additional HTML markup. As an icing on the cake, I've added a gentle micro-animation. I'm animating the bullet point's &lt;code&gt;box-shadow&lt;/code&gt; property and offsetting the odd and even bullet points by half of the animation period, thus creating an alternating effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;__event&lt;/span&gt;
    &lt;span class="err"&gt;…&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt;
      &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
      &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;
      &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$linkColor&lt;/span&gt;
      &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;
      &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt;
      &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;45deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="mi"&gt;.5s&lt;/span&gt;
      &lt;span class="nl"&gt;animation-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;microEvent&lt;/span&gt;
      &lt;span class="nl"&gt;animation-iteration-count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;
      &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;odd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt;
        &lt;span class="nl"&gt;animation-delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.75s&lt;/span&gt;

    &lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="nt"&gt;microEvent&lt;/span&gt;
      &lt;span class="nt"&gt;from&lt;/span&gt;
        &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nt"&gt;to&lt;/span&gt;
        &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.5rem&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$linkColor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Special thanks go to my wife for letting me use some of her artist's CV records to make the example a bit more natural and for the cover image. Please, reward her goodwill, visit her website &lt;a href="https://emmaplunkett.art"&gt;Emma Plunkett Art&lt;/a&gt; and perhaps even buy a piece of her artwork.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>tutorial</category>
      <category>css</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My story as a web developer – mid-senior level</title>
      <dc:creator>Honza</dc:creator>
      <pubDate>Wed, 06 May 2020 16:46:32 +0000</pubDate>
      <link>https://dev.to/crs1138/my-story-as-a-web-developer-mid-senior-level-21f3</link>
      <guid>https://dev.to/crs1138/my-story-as-a-web-developer-mid-senior-level-21f3</guid>
      <description>&lt;p&gt;I'd like to thank every one of you who gave me feedback on the first part of my dev's journey. One of the questions that came up, is why am I writing all this? The answer is, that I'd like to encourage web developers, that are at the beginning of their journey, to dive in and fully submerge in this beautiful world of infinite possibilities. My articles on the topic aim to provide you with a couple of points of reference that I, myself, found useful and entertaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web dev's puberty - my first co-working efforts
&lt;/h2&gt;

&lt;p&gt;After some time of playing with code on my own, I started to gain confidence in my skills. I started co-working with other people. It wasn't easy to find that many people that shared my passion for web development in the rural area where I lived. Usually, people didn't have a clue what I was talking about. Eventually, I got introduced to Ippy from &lt;a href="https://netuxo.coop" rel="noopener noreferrer"&gt;Netuxo Coop&lt;/a&gt;, who runs her small web development agency. She uses her contacts on the British and international activist scene to enable highly ethical NGOs to have a web presence and to pursue their online goals.&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%2Fi%2Fo8rbgjxx6gl3qz0xeum0.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%2Fi%2Fo8rbgjxx6gl3qz0xeum0.png" alt="CMS learning curve comparison - Drupal cliff"&gt;&lt;/a&gt;&lt;/p&gt;
Drupal's learning curve has even its own meme. What a legend!



&lt;p&gt;Netuxo focuses mainly on Drupal-based websites. This was a novelty for me. &lt;em&gt;Drupal's learning curve is legendary.&lt;/em&gt; However, I enjoyed working with Ippy and Andrea(s). Even though I maintained my freelance status, I was working more and more exclusively for Netuxo. I was helping them with regular Drupal updates, designing websites and user interfaces, and then turning these into static HTML/CSS/JS prototypes and subsequently developing full-featured Drupal 8 themes.&lt;/p&gt;

&lt;p&gt;It was then when I learnt about tooling. At first I was using my CSS pre-processor skills such as &lt;a href="https://sass-lang.com/" rel="noopener noreferrer"&gt;SCSS/Sass&lt;/a&gt;,&lt;a href="http://compass-style.org/" rel="noopener noreferrer"&gt;Compass&lt;/a&gt; (does anybody still use it?) and &lt;a href="http://lesscss.org/" rel="noopener noreferrer"&gt;Less&lt;/a&gt;. It quickly became obvious, I needed some reliable way of compiling the source code that was more sustainable than running their command line default compilers. Some sort of tool that would enable me to write modern code, whilst making it readable for older browsers.&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%2Fi%2F6qmbc1eto8xiwh57rlrx.jpg" 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%2Fi%2F6qmbc1eto8xiwh57rlrx.jpg" alt="Webpack configuration file"&gt;&lt;/a&gt;&lt;/p&gt;
An example of a Webpack config file, enabling me to merge different partial configurations into one.



&lt;p&gt;I started off with a small desktop app called &lt;a href="https://prepros.io/" rel="noopener noreferrer"&gt;Prepros&lt;/a&gt; – it's basically an app wrapper for Webpack and it does the job and does it well. The problem was, that I was the only one who could compile the code. Not an ideal situation in a collaborative context. The next obvious step was to learn &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;. There is actually a couple of really good free courses at the &lt;a href="https://webpack.academy/" rel="noopener noreferrer"&gt;Webpack Academy&lt;/a&gt;, these got me started. Eventually, I settled on using &lt;a href="https://laravel.com/docs/7.x/mix" rel="noopener noreferrer"&gt;Laravel's Mix&lt;/a&gt; for compiling front-end resources.&lt;/p&gt;

&lt;p&gt;This opened up my possibilities because I could now use Javascript ES6. To start with I wasn't sure what I was doing and started mixing ES6 over ES5 without even realising I was doing that. Yet, it worked – thank you &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt;! My ignorance was truly blissful. Around this time, I came across &lt;a href="https://twitter.com/wesbos" rel="noopener noreferrer"&gt;Wes Bos aka @wesbos&lt;/a&gt; and a couple of his courses. At first, I bumped into &lt;a href="https://javascript30.com/" rel="noopener noreferrer"&gt;Javascript30&lt;/a&gt;. A great exercise course showing the new ES6 features on practical examples. At the same time, the &lt;a href="https://es6.io/" rel="noopener noreferrer"&gt;ES6.io Master Package&lt;/a&gt; happened to be on sale. &lt;em&gt;Coincidence? I don't think so!&lt;/em&gt; I jumped at the opportunity. This was a game-changer for me. Javascript suddenly felt &lt;em&gt;so much easier&lt;/em&gt;. Wes is a brilliant teacher, he puts a lot of time into his courses and it shows. He can deliver the concept, not just the syntax and his examples and exercises are well-thought through. There are no confusing &lt;em&gt;foos&lt;/em&gt; and &lt;em&gt;bars&lt;/em&gt;, but real-life situations using multimedia and user interaction. In short, Wes' courses are fun.&lt;/p&gt;

&lt;blockquote&gt;

&lt;div class="podcastliquidtag"&gt;
  &lt;div class="podcastliquidtag__info"&gt;
    &lt;a href="/syntax/javascript-tooling--004"&gt;
      &lt;h1 class="podcastliquidtag__info__episodetitle"&gt;JavaScript Tooling - 004&lt;/h1&gt;
    &lt;/a&gt;
    &lt;a href="/syntax"&gt;
      &lt;h2 class="podcastliquidtag__info__podcasttitle"&gt;
        Syntax - Tasty Web Development Treats
      &lt;/h2&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div id="record-javascript-tooling--004" class="podcastliquidtag__record"&gt;
    &lt;img class="button play-butt" id="play-butt-javascript-tooling--004" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fplaybutt-5e444a2eae28832efea0dec3342ccf28a228b326c47f46700d771801f75d6b88.png" alt="play"&gt;
    &lt;img class="button pause-butt" id="pause-butt-javascript-tooling--004" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fpausebutt-bba7cb5f432cfb16510e78835378fa22f45fa6ae52a624f7c9794fefa765c384.png" alt="pause"&gt;
    &lt;img class="podcastliquidtag__podcastimage" id="podcastimage-javascript-tooling--004" alt="Syntax - Tasty Web Development Treats" 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%2Fpodcast%2Fimage%2F51%2Ffde96616-3607-4656-8ea2-a1756d0aaff1.png"&gt;
  &lt;/div&gt;

  &lt;div class="hidden-audio" id="hidden-audio-javascript-tooling--004"&gt;
  
    
    Your browser does not support the audio element.
  
  &lt;div id="progressBar" class="audio-player-display"&gt;
    &lt;a href="/syntax/javascript-tooling--004"&gt;
      &lt;img id="episode-profile-image" alt="JavaScript Tooling - 004" width="420" height="420" 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%2Fpodcast%2Fimage%2F51%2Ffde96616-3607-4656-8ea2-a1756d0aaff1.png"&gt;
      &lt;img id="animated-bars" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fanimated-bars-4e8c57c8b58285fcf7d123680ad8af034cd5cd43b4d9209fe3aab49d1e9d77b3.gif" alt="animated volume bars"&gt;
    &lt;/a&gt;
    &lt;span id="barPlayPause"&gt;
      &lt;img class="butt play-butt" alt="play" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fplaybutt-5e444a2eae28832efea0dec3342ccf28a228b326c47f46700d771801f75d6b88.png"&gt;
      &lt;img class="butt pause-butt" alt="pause" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fpausebutt-bba7cb5f432cfb16510e78835378fa22f45fa6ae52a624f7c9794fefa765c384.png"&gt;
    &lt;/span&gt;
    &lt;span id="volume"&gt;
      &lt;span id="volumeindicator" class="volume-icon-wrapper showing"&gt;
        &lt;span id="volbutt"&gt;
          &lt;img alt="volume" class="icon-img" height="16" width="16" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fvolume-cd20707230ae3fc117b02de53c72af742cf7d666007e16e12f7ac11ebd8130a7.png"&gt;
        &lt;/span&gt;
        &lt;span class="range-wrapper"&gt;
          
        &lt;/span&gt;
      &lt;/span&gt;
      &lt;span id="mutebutt" class="volume-icon-wrapper hidden"&gt;
        &lt;img alt="volume-mute" class="icon-img" height="16" width="16" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fvolume-mute-8f08ec668105565af8f8394eb18ab63acb386adbe0703afe3748eca8f2ecbf3b.png"&gt;
      &lt;/span&gt;
      &lt;span class="speed" id="speed"&gt;1x&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class="buffer-wrapper" id="bufferwrapper"&gt;
      &lt;span id="buffer"&gt;&lt;/span&gt;
      &lt;span id="progress"&gt;&lt;/span&gt;
      &lt;span id="time"&gt;initializing...&lt;/span&gt;
      &lt;span id="closebutt"&gt;×&lt;/span&gt;
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;
Wes teams up with &lt;a href="https://twitter.com/stolinski" rel="noopener noreferrer"&gt;Scott Tolinski aka @stolinski&lt;/a&gt; and between them, they produce a podcast called &lt;a href="https://syntax.fm/" rel="noopener noreferrer"&gt;Syntax.fm&lt;/a&gt;. It's a great resource of knowledge about dev's technologies, technique's and also bbq tips. They are really entertaining – I highly recommend listening to them.
&lt;/blockquote&gt;

&lt;p&gt;Whilst working with Netuxo I got to use the command-line interface (CLI) much more, as a lot of work was done remotely on their development servers. It also started to make sense to use some sort of Git branch-based workflow. Being a small team, we sometimes got away with forgetting to be meticulous about implementing it. &lt;/p&gt;

&lt;p&gt;There were periods of downtime with Netuxo, which usually coincided with the summer season as the rest of the team went away for their holidays. These periods would last for about two to three months. I had time for self-study. Working on my own projects was becoming more and more scarce, because I didn't chase any new leads, so I found myself more often participating in other people's projects during this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://knowledgeisland.org/" rel="noopener noreferrer"&gt;Knowledge Island&lt;/a&gt; – my mid-level graduation
&lt;/h2&gt;

&lt;p&gt;There is one project I worked on during one of these (quieter) periods that I think is worth writing home about. I was recommended by Ippy to her ex-coworker, Joris. He was looking for someone to help him develop a new feature, for a web-based application, that helps young kids in the USA to fight the problem of childhood obesity. Joris was just too busy with other features, that he was happy to outsource this one to me.&lt;/p&gt;

&lt;p&gt;The brief was to develop an online web application that would enable children to create personalised workout routines. They got to choose from a selection of 10 videos, each with different exercises. They can set the length for each exercise from three or four options and choose how many repetitions for each video. This would then generate a single video stream made up from a generic intro video, followed by the selected exercise videos (with the right amount of repetitions) and finished off with an encouraging outro video. All this would be saved using Ajax into the Joomla based main apps database. &lt;/p&gt;

&lt;p&gt;I had literally zero Joomla experience. After struggling with Drupal for what was by then about a year and a half, I made it clear with Joris, &lt;em&gt;"I'm happy to do the frontend part and even create the API interface but you will have to tie it all into Joomla yourself."&lt;/em&gt; He agreed… phew!&lt;/p&gt;

&lt;p&gt;I was a bit unsure about one particular aspect – Joris and his client Tony insisted on hosting the videos themselves. Prior to this gig, I had no experience of using the browser's video API. I always opted for some sort of 3rd party video player, whether it was Youtube, Vimeo or JW Video Player (when it was still in its infancy). I still said yes to the gig, thinking I'll just have to wing it. We compromised on using the &lt;a href="https://videojs.com/" rel="noopener noreferrer"&gt;Video.js&lt;/a&gt; library to make it easier to deal with the stream part of the task.&lt;/p&gt;

&lt;p&gt;And I winged it! I designed a responsive web app giving the users (kids) an intuitive interface based on drag&amp;amp;drop functionality. The result is smooth playing in-app generated video playlists that are optimised not only for various screen sizes but also for different bandwidths, as the kids often use mobile devices more than computers.&lt;/p&gt;

&lt;p&gt;I can't really use that for my portfolio as it's behind a paywall and relies on copyrighted material for the video stream, but this was a significant project for me. It gave me the confidence that (with just an occasional consultation with the technical lead) I can tackle on my own and deliver on time, a project of this size and complexity. I've learnt that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You shall face your fear of handling complex projects. Take them on, even if you don't know all the answers when starting. Analyze them, break them into smaller manageable chunks and deal with each chunk separately.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Moving on up…
&lt;/h2&gt;

&lt;p&gt;In 2019, there was another significant length of downtime for me, in terms of my work with Netuxo. In the early summer, we finished an important project – an upgrade of a website providing access to NGOs and their lawyers to all British immigration-related court cases. More than 60,000 pages, some with many various taxonomies. I was responsible for the development of a large part of the Drupal 8 theme.&lt;/p&gt;

&lt;p&gt;Once we launched, we all went for holidays in different parts of the world, thinking there will be more work from early autumn. As the summer was ending, there was bad news about projects being delayed, put on hold or some even cancelled. Our family finances were not ready for this kind of delay. I had not been chasing any of my own work leads for about two years by this time, so my funnel was pretty empty. I didn't actually want to do yet another artist's portfolio website anyway. There is no challenge for me doing that type of work, apart from explaining to the artist that their budget does not really allow for changing the way people navigate the internet and that they should instead focus on displaying their art and developing an online marketing strategy.&lt;/p&gt;

&lt;p&gt;This is when I updated all my professional online accounts, wrote down my CV and sent it out to local contacts and then eventually to the world. It was time to up my game and work on a bigger project. I had been yearning to find my geek tribe - to be part of a bigger team, where the roles are more defined. But that is another story, one that I'm planning to publish soon.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Header image: Andalusian landscape – Bridge, Emma Plunkett Art © 2019&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>journey</category>
    </item>
  </channel>
</rss>
