<?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: Eckhardt</title>
    <description>The latest articles on DEV Community by Eckhardt (@eckhardtd).</description>
    <link>https://dev.to/eckhardtd</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%2F148127%2Fe77c09f0-7219-4a18-9354-98ba09480df2.jpg</url>
      <title>DEV Community: Eckhardt</title>
      <link>https://dev.to/eckhardtd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eckhardtd"/>
    <language>en</language>
    <item>
      <title>Add Push Notifications to an iOS PWA (w / Nuxt &amp; FCM)</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Wed, 07 Feb 2024 09:43:27 +0000</pubDate>
      <link>https://dev.to/eckhardtd/add-push-notifications-to-an-ios-pwa-w-nuxt-fcm-3pmk</link>
      <guid>https://dev.to/eckhardtd/add-push-notifications-to-an-ios-pwa-w-nuxt-fcm-3pmk</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/YVl87Qw_yzk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I built a startup with Nuxt 3, Fastify and Zod - watch me add a feature live 🥲</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Sat, 28 Jan 2023 06:20:00 +0000</pubDate>
      <link>https://dev.to/eckhardtd/i-built-a-startup-with-nuxt-3-fastify-and-zod-watch-me-add-a-feature-live-nlg</link>
      <guid>https://dev.to/eckhardtd/i-built-a-startup-with-nuxt-3-fastify-and-zod-watch-me-add-a-feature-live-nlg</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/kV6MfFu45FM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>fastify</category>
      <category>saas</category>
    </item>
    <item>
      <title>Maintain / improve website performance with Lighthouse and Auditio</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Fri, 20 Jan 2023 06:16:13 +0000</pubDate>
      <link>https://dev.to/eckhardtd/maintain-improve-website-performance-with-lighthouse-and-auditio-3819</link>
      <guid>https://dev.to/eckhardtd/maintain-improve-website-performance-with-lighthouse-and-auditio-3819</guid>
      <description>&lt;h2&gt;
  
  
  The web is moving towards performance
&lt;/h2&gt;

&lt;p&gt;When you look at the current web ecosystem, you'll see players like &lt;a href="https://deno.land" rel="noopener noreferrer"&gt;Deno&lt;/a&gt;, &lt;a href="https://bun.sh" rel="noopener noreferrer"&gt;Bun&lt;/a&gt;, &lt;a href="https://qwik.builder.io" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt; all pushing the frontier on performance in their own domain.&lt;/p&gt;

&lt;p&gt;There is growing chatter in the development community regarding load times, content repaints, JS payloads etc. and though it's growing - not a lot of people care much for it.&lt;/p&gt;

&lt;p&gt;Many developers, web agencies and / or frameworks still focus on product-first instead of overthinking optimizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed is product, but there's a better way
&lt;/h2&gt;

&lt;p&gt;It's no secret that &lt;a href="https://blog.hubspot.com/marketing/page-load-time-conversion-rates" rel="noopener noreferrer"&gt;some sort of correlation&lt;/a&gt; exists between load times and conversions / sales and I think it's fair to argue that speed is a part of your product offering's KSP.&lt;/p&gt;

&lt;p&gt;It's not always feasible to be performance-first focused when launching a new product or MVP of a product. This is where I think the &lt;a href="https://en.wikipedia.org/wiki/Kaizen" rel="noopener noreferrer"&gt;Kaizen&lt;/a&gt; philosophy is a great tool.&lt;/p&gt;

&lt;p&gt;Using the principles of &lt;em&gt;Continuous Improvement&lt;/em&gt;, we can instead incrementally improve what matters. That is to say, we have a faster go-to-market and then we optimize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sounds like a job for Auditio
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://auditio.cc" rel="noopener noreferrer"&gt;Auditio&lt;/a&gt; is a tool that provides us a way to do this. It allows you to add your website's URL and then set a frequency at which you want to run a Google Lighthouse audit on that URL. Just set, forget and check periodically.&lt;/p&gt;

&lt;p&gt;Every day / week / month you are provided with a snapshot of your website's current performance. Using this we can implement the Kaizen philosophy and say: "We don't want 100% performance right now, but we want to at least know what we can do to be a little bit faster than yesterday or last week".&lt;/p&gt;

&lt;p&gt;This approach, to me, seems much more sustainable than spending an exorbitant amount of time to optimize a site before it even goes to market to be validated in terms of utility and content.&lt;/p&gt;

&lt;h2&gt;
  
  
  How fast should your website be?
&lt;/h2&gt;

&lt;p&gt;I don't think a 100 score on every page is a healthy / realistic target, neither do I think 25 is great. To me the middle ground of improving what you can and maintaining what is good seems much better.&lt;/p&gt;

&lt;p&gt;Let me know what your page ranks at! Do you think Auditio can be a useful tool?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note, right now there is a free offering at Auditio during their Beta stage. Check it out at &lt;a href="https://auditio.cc" rel="noopener noreferrer"&gt;https://auditio.cc&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webperf</category>
      <category>lighthouse</category>
      <category>auditio</category>
    </item>
    <item>
      <title>Setting up Nuxt3 for Scale with Turborepo and TDD</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Fri, 05 Aug 2022 11:09:14 +0000</pubDate>
      <link>https://dev.to/eckhardtd/setting-up-nuxt3-for-scale-with-turborepo-and-tdd-1h</link>
      <guid>https://dev.to/eckhardtd/setting-up-nuxt3-for-scale-with-turborepo-and-tdd-1h</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Q6CK3LyYqmM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Nuxt 3's useLazyAsyncData is a MAGIC function 🪄</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Tue, 15 Mar 2022 13:16:42 +0000</pubDate>
      <link>https://dev.to/eckhardtd/nuxt-3s-uselazyasyncdata-is-a-magic-function-p2c</link>
      <guid>https://dev.to/eckhardtd/nuxt-3s-uselazyasyncdata-is-a-magic-function-p2c</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/bhjwGTVwEpc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build an INSANELY easy Vue/Nuxt 3 UI library published on NPM in 3 minutes 🚀</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Sat, 12 Mar 2022 09:52:38 +0000</pubDate>
      <link>https://dev.to/eckhardtd/build-an-insanely-easy-nuxt-3-ui-library-published-on-npm-in-3-minutes-lo4</link>
      <guid>https://dev.to/eckhardtd/build-an-insanely-easy-nuxt-3-ui-library-published-on-npm-in-3-minutes-lo4</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/5BcJ2WUTltU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>nuxt3</category>
      <category>nuxt</category>
      <category>ui</category>
    </item>
    <item>
      <title>Building a fully automatic https developer profile generator with Nuxt.js, Caddy, Typescript, Sequelize and more.</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Tue, 08 Mar 2022 10:45:38 +0000</pubDate>
      <link>https://dev.to/eckhardtd/building-a-fully-automatic-https-developer-profile-generator-with-nuxtjs-caddy-typescript-sequelize-and-more-4k3g</link>
      <guid>https://dev.to/eckhardtd/building-a-fully-automatic-https-developer-profile-generator-with-nuxtjs-caddy-typescript-sequelize-and-more-4k3g</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Persona
&lt;/h2&gt;

&lt;p&gt;One public profile to rule them all. By developers for developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;Persona was developed for the &lt;a href="https://dev.to/devteam/hack-the-microsoft-azure-trial-on-dev-2ne5"&gt;dev.to&lt;/a&gt; Microsoft Azure hackathon. It's a mono-repo TypeScript project featuring many things like GitHub OAuth, Mono Repo architecture, Database Management, API servers, Full-Stack Modern JS framework apps, complicated networking concepts through allowing users to point their own domains to a DNS and view their public profile on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/services/virtual-machines/" rel="noopener noreferrer"&gt;Microsoft Azure VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/automatic-https" rel="noopener noreferrer"&gt;Caddy auto HTTPS and reverse proxying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxtjs.org" rel="noopener noreferrer"&gt;Nuxt.js and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sequelize.org/master/manual/migrations.html" rel="noopener noreferrer"&gt;SQlite Database with Sequelize Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com" rel="noopener noreferrer"&gt;Github Oauth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastify.io" rel="noopener noreferrer"&gt;Fastify REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com" rel="noopener noreferrer"&gt;Express static site server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the app works
&lt;/h2&gt;

&lt;p&gt;You access the app at &lt;a href="https://azure.kaizen.com.na" rel="noopener noreferrer"&gt;https://azure.kaizen.com.na&lt;/a&gt;. Here you can authorize the application with GitHub to create a base profile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a custom domain
&lt;/h3&gt;

&lt;p&gt;The input to add a custom domain e.g. &lt;code&gt;profile.example.com&lt;/code&gt; will allow you to save a custom domain to your profile, once you CNAME this domain to &lt;code&gt;dns-azure.kaizen.com.na&lt;/code&gt;, your public profile will be accessible there. (by default it's accessible by &lt;code&gt;https://static-azure.kaizen.com.na/&amp;lt;your-user-id&amp;gt;&lt;/code&gt;, which is accessible on your dashboard).&lt;/p&gt;

&lt;h3&gt;
  
  
  Try my custom domain 😀
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://profile.kaizen.com.na" rel="noopener noreferrer"&gt;https://profile.kaizen.com.na&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Update your profile
&lt;/h3&gt;

&lt;p&gt;Currently you can update your:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name, that will be displayed on your public page&lt;/li&gt;
&lt;li&gt;Bio (Markdown enabled), which can be previewed by click the 'toggle preview' button when in 'edit mode'&lt;/li&gt;
&lt;li&gt;Website (not currently used in public)&lt;/li&gt;
&lt;li&gt;Profile Image, You can upload a new profile image in edit mode (defaults to your github image). This is shown on your public page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The markdown editor doesn't support full markdown features. For security reasons and not having the time to do proper sanitisations I've disabled html.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Notes
&lt;/h2&gt;

&lt;p&gt;The code is pretty much reusable outside of some things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard coded url values e.g. &lt;code&gt;https://azure.kaizen.com.na&lt;/code&gt;: you'll have to update these to yours.&lt;/li&gt;
&lt;li&gt;ENV variables: I have left some &lt;code&gt;.env.dist&lt;/code&gt; files where necessary so you know what is required to run the app.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Eckhardt-D/persona.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note: The &lt;code&gt;VM&lt;/code&gt; runs a Caddy server. This allows for easy config on auto-https and reverse proxying, install &lt;a href="https://caddyserver.com" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt; on your VM and edit your Caddyfile to this (just update the hard coded domains to your own and make sure they point to your instance IP):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    on_demand_tls {
        ask      http://localhost:3002/domain/verify
        interval 2m
        burst    5
    }
}

https:// {
  tls {
    on_demand
  }

  reverse_proxy {
    to http://localhost:3001
    header_up Host {upstream_hostport}
    header_up X-Forwarded-For {host}
  }
}

azure.kaizen.com.na {
  tls internal
  reverse_proxy http://localhost:3000
}

api-azure.kaizen.com.na {
  tls internal
  reverse_proxy http://localhost:3002
}

static-azure.kaizen.com.na {
  tls internal
  reverse_proxy {
    to http://localhost:3001
    header_up Host {upstream_hostport}
    header_up X-Forwarded-For static-azure
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As simple as that, the monorepo was setup with DX in mind. Although there could be some nuances that'll have to be referred to by opening an issue to this. Hope you enjoy it and feel free to contribute.&lt;/p&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Computing Captains&lt;/p&gt;
&lt;h3&gt;
  
  
  Link to Code on GitHub
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Eckhardt-D" rel="noopener noreferrer"&gt;
        Eckhardt-D
      &lt;/a&gt; / &lt;a href="https://github.com/Eckhardt-D/persona" rel="noopener noreferrer"&gt;
        persona
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Source code for the Microsoft Azure dev.to competition
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Persona&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;One public profile to rule them all. By developers for developers.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Description&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Persona was developed for the &lt;a href="https://dev.to/devteam/hack-the-microsoft-azure-trial-on-dev-2ne5" rel="nofollow"&gt;dev.to&lt;/a&gt; Microsoft Azure hackathon. It's a mono-repo TypeScript project featuring many things like GitHub OAuth, Mono Repo architecture, Database Management, API servers, Full-Stack Modern JS framework apps, complicated networking concepts through allowing users to point their own domains to a DNS and view their public profile on it.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/services/virtual-machines/" rel="nofollow noopener noreferrer"&gt;Microsoft Azure VM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caddyserver.com/docs/automatic-https" rel="nofollow noopener noreferrer"&gt;Caddy auto HTTPS and reverse proxying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxtjs.org" rel="nofollow noopener noreferrer"&gt;Nuxt.js and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sequelize.org/master/manual/migrations.html" rel="nofollow noopener noreferrer"&gt;SQlite Database with Sequelize Migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com" rel="noopener noreferrer"&gt;Github Oauth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastify.io" rel="nofollow noopener noreferrer"&gt;Fastify REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com" rel="nofollow noopener noreferrer"&gt;Express static site server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How the app works&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;You access the app at &lt;a href="https://azure.kaizen.com.na" rel="nofollow noopener noreferrer"&gt;https://azure.kaizen.com.na&lt;/a&gt; [Instance Has Been Shutdown]. Here you can authorize the application with GitHub to create a base profile.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Add a custom domain&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;The input to add a custom domain e.g. &lt;code&gt;profile.example.com&lt;/code&gt; will allow you to save a custom domain to your profile, once you CNAME this domain to…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Eckhardt-D/persona" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

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

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

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

</description>
      <category>azuretrialhack</category>
      <category>microsoft</category>
      <category>azure</category>
    </item>
    <item>
      <title>Build an Automated Nuxt Content Blog with Firebase and Github</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Thu, 16 Dec 2021 03:04:34 +0000</pubDate>
      <link>https://dev.to/eckhardtd/build-an-automated-nuxt-content-blog-with-firebase-and-github-oc0</link>
      <guid>https://dev.to/eckhardtd/build-an-automated-nuxt-content-blog-with-firebase-and-github-oc0</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4utfS0jfUbo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>showdev</category>
    </item>
    <item>
      <title>SML - The language I love to hate. (Recursion example).</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Wed, 13 Oct 2021 16:46:33 +0000</pubDate>
      <link>https://dev.to/eckhardtd/sml-the-language-i-love-to-hate-recursion-example-mem</link>
      <guid>https://dev.to/eckhardtd/sml-the-language-i-love-to-hate-recursion-example-mem</guid>
      <description>&lt;p&gt;Standard ML has been around for a while, I never cared for it until enrolling at &lt;a href="https://github.com/ossu/computer-science" rel="noopener noreferrer"&gt;OSSU's program&lt;/a&gt;. It's a functional programming language with very interesting features like &lt;code&gt;immutability&lt;/code&gt; and relies heavily on the usage of recursion.&lt;/p&gt;

&lt;h2&gt;
  
  
  A recursive example in SML
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sml"&gt;&lt;code&gt;&lt;span class="kr"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;-&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;p&gt;This function can also be expressed as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sml"&gt;&lt;code&gt;&lt;span class="kr"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
    &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;*&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;-&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;p&gt;If you use &lt;code&gt;factorial&lt;/code&gt; it may look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sml"&gt;&lt;code&gt;&lt;span class="kr"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;(*&lt;/span&gt;&lt;span class="cm"&gt; equivalent: 5 x 4 x 3 x 2 x 1 *)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in actual fact, this function actually (cheekily) multiplies by 1 again on the last iteration, which has no effect on the output: &lt;code&gt;120&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing SML as a web developer
&lt;/h2&gt;

&lt;p&gt;During the course assignments it was a REAL pain to not have the ability to declare variables in the upper scope and change their value, or use loops, and dealing with the thinking work that goes into recursion. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hot tip: If a function accepts an Array / List that needs to be processed in a loop - it most probably can be done with recursion too. 🤘&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A JS example re-written in SML
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Return the sum of all elements in an array&lt;/em&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;// Example 1&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sumArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&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;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;nums&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;sumArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;// 15&lt;/span&gt;

&lt;span class="c1"&gt;// Example 2&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sumArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&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;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curr&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;prev&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;sumArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;// 15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much of the logic above is either ludicrous or doesn't exist in SML, and we'll have to do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sml"&gt;&lt;code&gt;&lt;span class="kr"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;sum_array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;int&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
  &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;
  &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;hd&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="n"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sum_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tl&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;summed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum_array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c"&gt;(*&lt;/span&gt;&lt;span class="cm"&gt; 15 *)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These were just a few quick examples. If you want to see me explain these things (a little) more and If you want to see me do this with 5 slightly more complex problems, give my latest Youtube video a watch:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/pUh-_Ba_jUY"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;em&gt;Appreciate the 👍&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I think we developers should definitely focus on the language that is most demanded from us - to improve our skills / knowledge, nevertheless - SML has taught me that there are different and sometimes better and &lt;em&gt;cleaner&lt;/em&gt; ways of solving programming issues.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This has been Eckhardt Dreyer, coming to you with a look into what I explore on the web. If you like what I make &lt;a href="https://kaizen.com.na/payment?ref=dono&amp;amp;amt=10000" rel="noopener noreferrer"&gt;Buy me a coffee? 🍺&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sml</category>
      <category>javascript</category>
      <category>recursion</category>
    </item>
    <item>
      <title>Downloading images in the browser with Node.js</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Thu, 09 Sep 2021 08:54:54 +0000</pubDate>
      <link>https://dev.to/eckhardtd/downloading-images-in-the-browser-with-node-js-4f0h</link>
      <guid>https://dev.to/eckhardtd/downloading-images-in-the-browser-with-node-js-4f0h</guid>
      <description>&lt;p&gt;Image downloading in modern browsers seems like a topic trivial enough - why write about it?&lt;/p&gt;

&lt;h3&gt;
  
  
  The drawback of native HTML downloading
&lt;/h3&gt;

&lt;p&gt;HTML5 has a neat &lt;code&gt;download&lt;/code&gt; attribute readily available for the anchor element - by simply adding the following, a user can easily view the image by clicking the link.&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://picsum.photos/200/300"&lt;/span&gt; &lt;span class="na"&gt;download=&lt;/span&gt;&lt;span class="s"&gt;"random.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Download this image
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this approach is that the image simply opens in the browser and requires the user to &lt;code&gt;save as&lt;/code&gt;. This behaviour may not be the preferable user experience. A better flow may be that the user clicks the link and it automatically downloads into the Download Folder configured in the browser's settings.&lt;/p&gt;

&lt;p&gt;This is also achievable without any server-side code in the following way:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;index.html&lt;/em&gt;&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;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"download-link"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Download Image&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;index.js&lt;/em&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;downloadButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#download-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;downloadButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&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;evt&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;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch the image blob&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://picsum.photos/200/300&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Create an objectURL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blobURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// create a hidden anchor element&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the &amp;lt;a&amp;gt; tag's href to blob url&lt;/span&gt;
  &lt;span class="c1"&gt;// and give it a download name&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blobURL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image-name.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Append anchor and trigger the download&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&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 client-side code above listens to a click on the HTML button, fetches the image as a blob, creates an objectURL, adds it to a newly created (hidden) anchor tag and clicks it to initiate a download. Because the anchor tag has an object URL, the browser will initiate the download to the user's Download Folder.&lt;/p&gt;

&lt;p&gt;This experience may be more user-friendly, but don't be surprised if you run into the notorious &lt;code&gt;CORS&lt;/code&gt; wall. &lt;code&gt;CORS&lt;/code&gt; or Cross-Origin Resource Sharing may many times cause the download to fail from the browser if the resource is not on the same origin, or doesn't have the appropriate headers set.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making image download robust with Node.js
&lt;/h3&gt;

&lt;p&gt;Luckily, for requests not coming from a browser e.g. a Node.js server - &lt;code&gt;CORS&lt;/code&gt; can be safely bypassed. The following example only requires one simple change to the download logic on the client - the URL. Instead of making a fetch directly to the image, you will make it to your Node.js API endpoint, which could be set up as follows:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app.js&lt;/em&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;fetch&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="s2"&gt;node-fetch&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;express&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="s2"&gt;express&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;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="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="s2"&gt;/image&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Fetch the required image&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://picsum.photos/200/300&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageURL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the appropriate headers, to let&lt;/span&gt;
  &lt;span class="c1"&gt;// the browser know that it should save&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-disposition&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;attachment; filename="my-image.png"&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;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="s2"&gt;image/png&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;// Pipe the request buffer into&lt;/span&gt;
  &lt;span class="c1"&gt;// the response back to the client&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above has a few parts to it, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requesting the known image URL to receive the raw body in the response. The URL here could be dynamically set too and that way you could simply prepend your server URL to &lt;em&gt;any&lt;/em&gt; image URL, e.g.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/image/:url&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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;p&gt;Just remember to encode the URI on the client before appending it to your server URL, e.g.&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;finalURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://your-server.com/image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Setting the appropriate headers for the response:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;content-dispostion&lt;/code&gt; with a value of &lt;code&gt;attachment&lt;/code&gt; will tell the browser to save the file instead of the alternative &lt;code&gt;inline&lt;/code&gt; which will try to render the response in the browser.&lt;/p&gt;

&lt;p&gt;Note here too you might want to have some sort of library or checker to determine the image MIME type e.g. &lt;code&gt;image/png&lt;/code&gt; for the &lt;code&gt;content-type&lt;/code&gt; header and file extension to be accurate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Piping the result into the response:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simply takes the data in the result body and feeds it into the body of the response to the client.&lt;/p&gt;

&lt;h4&gt;
  
  
  Serverless Caveat
&lt;/h4&gt;

&lt;p&gt;If you're using a serverless solution, be mindful of their Request Payload size limits. E.g. AWS limits the size of request bodies to ~6MB. If you're working with large images, consider a static solution.&lt;/p&gt;

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

&lt;p&gt;If you're already calling a Node.js back-end to feed your front-end, why not add an endpoint to help you download remote images with a better experience. You even get the niceties of overcoming the dreaded &lt;code&gt;CORS&lt;/code&gt; error.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you want to automate this task for your website screenshots, let &lt;a href="https://www.stillio.com" rel="noopener noreferrer"&gt;Stillio&lt;/a&gt; do the heavy lifting.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>images</category>
    </item>
    <item>
      <title>Why Promise.all() is not always a suitable solution. ⚡💡</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Thu, 15 Apr 2021 10:04:56 +0000</pubDate>
      <link>https://dev.to/stillio/why-promise-all-is-not-always-a-suitable-solution-6hi</link>
      <guid>https://dev.to/stillio/why-promise-all-is-not-always-a-suitable-solution-6hi</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Here at Stillio, it's paramount that we take care of the code we write. We process thousands of images and people rely on us for making sure their screenshots get delivered.&lt;/p&gt;

&lt;p&gt;Recently, we found something that might be interesting about &lt;code&gt;Promise.all()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Promise.all?
&lt;/h2&gt;

&lt;p&gt;The .all method on the Promise object is a neat way to process a list of asynchronous actions in order and receive an Array of results. e.g.&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;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Promise1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Promise2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Promise3&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// results: [Promise1Result, Promise2Result, Promise3Result...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A handy feature of Promise.all is that it will give back the results in the order that the promise array was given, let's look at a more robust example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;asyncAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5000&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;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;asyncAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;start&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Results: &lt;/span&gt;&lt;span class="dl"&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="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;Above we have a fake &lt;code&gt;urlArray&lt;/code&gt; variable. Assume that this is actual urls that we want to fetch. Since fetching is an async event we simulate that with the function &lt;code&gt;asyncAction&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All &lt;code&gt;asyncAction&lt;/code&gt; actually does is return a promise which resolves at a random interval between 0 and 5000ms. This is to simulate changes in network latency, not all requests respond in the exact same time.&lt;/p&gt;

&lt;p&gt;To create an Array of Promises, we map the &lt;code&gt;urlArray&lt;/code&gt; and return a new Array with the Promises after calling &lt;code&gt;asyncAction&lt;/code&gt;. These aren't resolved yet, so we use Promise.all() in &lt;code&gt;start()&lt;/code&gt; to give us an Array of results and since the function just returns the number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Results: &lt;/span&gt;&lt;span class="dl"&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="c1"&gt;// Results: [1, 2, 3, 4,  5, 6,  7, 8, 9, 10, 11, 12]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results demonstrate that even though each request took a considerable different amount of time to complete, the Promise.all() method made sure that the results were returned in the correct order, hooray there's reliability! Or is there...&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Promise.all() gets interesting
&lt;/h2&gt;

&lt;p&gt;Let's look at the above example again, but this time we change some things up. In the fake request let's update the resolve function to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;asyncAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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="nf"&gt;resolve&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;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5000&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;We've added a date component to the result of the Promise. This is actually something that we required in our code base. We had to process multiple URLs in an Array asynchronously and give it a timestamp that was congruent to the order of the Array. Sounds like the perfect job for Promise.all().&lt;/p&gt;

&lt;p&gt;Let's run the code again with the timestamp component included, here's the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Results:  [
  '1: 2021-04-15T07:55:28.315Z',
  '2: 2021-04-15T07:55:27.586Z',
  '3: 2021-04-15T07:55:30.063Z',
  '4: 2021-04-15T07:55:30.707Z',
  '5: 2021-04-15T07:55:28.549Z',
  '6: 2021-04-15T07:55:30.284Z',
  '7: 2021-04-15T07:55:27.231Z',
  '8: 2021-04-15T07:55:29.844Z',
  '9: 2021-04-15T07:55:26.287Z',
  '10: 2021-04-15T07:55:27.308Z',
  '11: 2021-04-15T07:55:28.798Z',
  '12: 2021-04-15T07:55:30.978Z'
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, Item 10 was created ~1s before item 1 was created. Even though the method maintained the order of the Promise resolutions, there was this effect that timestamps were dependent on these random intervals.&lt;/p&gt;

&lt;p&gt;This makes sense if you think about it, because fundamentally the issue already arose in the loop - when generating the promises. The loop doesn't wait for the previous promise to arrive for the next one to be made - it's still just a Promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative approach
&lt;/h2&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;urlArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;asyncAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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="nf"&gt;resolve&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;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5000&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;urlArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;asyncAction&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="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;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The code above is very similar, except we've removed promise.all() and replaced it with a simple &lt;code&gt;for of&lt;/code&gt; loop. On each iteration, we HAVE to wait for the previous result, because of &lt;code&gt;await&lt;/code&gt;. The outcome is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  '0: 2021-04-15T08:12:13.026Z', 
  '1: 2021-04-15T08:12:17.042Z', 
  '2: 2021-04-15T08:12:21.314Z', 
  '3: 2021-04-15T08:12:21.600Z', 
  '4: 2021-04-15T08:12:22.984Z', 
  '5: 2021-04-15T08:12:27.061Z', 
  '6: 2021-04-15T08:12:28.771Z', 
  '7: 2021-04-15T08:12:30.432Z', 
  '8: 2021-04-15T08:12:31.530Z', 
  '9: 2021-04-15T08:12:35.691Z', 
  '10: 2021-04-15T08:12:40.394Z',
  '11: 2021-04-15T08:12:41.410Z'
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are ordered and so is the time component. This was what was actually expected.&lt;/p&gt;

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

&lt;p&gt;Promise.all() is a super handy tool but because of it's abstraction, it's easy to overlook something like a time component. Keep in mind, the first example ran much quicker than the second, since the timers didn't need to complete before the next result was fired.&lt;/p&gt;

&lt;p&gt;So in general, if there is an element of time. e.g. a &lt;code&gt;dateCreated&lt;/code&gt; property on the results of a list of Promises or asynchronous actions, consider using a loop instead of Promise.all().&lt;/p&gt;




&lt;p&gt;by &lt;strong&gt;Eckhardt Dreyer&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Frontend Engineer&lt;/em&gt;&lt;br&gt;
&lt;a href="https://stillio.com" rel="noopener noreferrer"&gt;Stillio&lt;/a&gt; automated website screenshots&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>promises</category>
    </item>
    <item>
      <title>Dealing with novel emotions as a new-born developer</title>
      <dc:creator>Eckhardt</dc:creator>
      <pubDate>Mon, 12 Apr 2021 07:59:14 +0000</pubDate>
      <link>https://dev.to/eckhardtd/dealing-with-novel-emotions-as-a-new-born-developer-3hhj</link>
      <guid>https://dev.to/eckhardtd/dealing-with-novel-emotions-as-a-new-born-developer-3hhj</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by cottonbro from Pexels&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The honeymoon phase of software development
&lt;/h2&gt;

&lt;p&gt;This stage is where the proverbial hook sets and you're spending most of your time doing courses, watching live streams or thinking about what cool thing you can build with your new-found love for this art that is software development.&lt;/p&gt;

&lt;p&gt;Some are, at this point, already working as an intern / junior and others (most) aren't in the field professionally yet - but the experience is similar. It kind of feels like there's no limit to what you want to learn or build.&lt;/p&gt;

&lt;p&gt;Hopefully here is where you have used your energy and motivation to employ your skills towards a goal of actually making a living from it. This is the best time to pivot, because you'll want to head each problem UNTIL you have figured it out. This trait is well sought out in this industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  From novel to monotony
&lt;/h2&gt;

&lt;p&gt;One day you're at the computer, looking at the same code that you've seen for the past few months. Repeating the same patterns, reporting to the same people - git add, git commit, git repeat.&lt;/p&gt;

&lt;p&gt;The novelty of it all wanes and all of a sudden you're not watching your usual Youtube tutorial before bed, you're not waking up and sketching some idea that came to you. You're just going through the motions.&lt;/p&gt;

&lt;p&gt;People around you don't really seem to get what you're doing either, they just assume you're in IT - so they assume that your dark circles and depressed attitude comes with the trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Burnout in software development
&lt;/h2&gt;

&lt;p&gt;Then Finally it comes to the point of no return and you burn out. Suddenly there's no interest in something you once loved SO much. At this point I quit software for over a year and barely ever even opened an IDE. Some grind on, others quit and some handle it even worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery from software burnout
&lt;/h2&gt;

&lt;p&gt;This is where I hope to give some insights from my own experience, chances are not many are reading this post unless they feel like they have to. So if you feel that way - I hope the following advice from my personal experience can help you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;First of all if you're experiencing serious mental health problems in your career or life. Please seek professional help - there is no substitute for trained counseling.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  (Prevention &amp;gt; cure) for burnout
&lt;/h2&gt;

&lt;p&gt;Recognizing that you are losing vigor and a lust for life is one of the most important steps in preventing burnout. It is usually when I ignore the signs where it gets ugly. Take a 5 minute self-evaluation every day and ask yourself 'Am I ignoring things that I should be focusing on with regards to my health and energy?'.&lt;/p&gt;

&lt;p&gt;This is not to make excuses to yourself about not really feeling like getting up or general life challenges. But be honest and ask whether you are still excited about your favorite aspects of software? Is something draining you and causing you to push away your love for the skill? Then work to address that thing as early as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluate the people you're working with
&lt;/h2&gt;

&lt;p&gt;Sometimes it's not the work or mundane aspect of it all. It could be that a person is causing this pre-frontal cortex drain and sapping all valuable energy you could have employed into your work. This could be the absolute ***hole of a senior completely destroying your self-worth, by degrading your work through condescension, or on the flip-side, the junior who just isn't getting it and you have to keep fixing their problems.&lt;/p&gt;

&lt;p&gt;Both these situations can be prepared for, by setting out time for work first and then preparing an hour of expected mental pressure from people around you. Accept that you and other people may sometimes suck, but prepare for addressing it structurally.&lt;/p&gt;

&lt;p&gt;Ask members to respect your time when you're working and reserve critical interaction for planned spaces. In this time put on your 'Interacting with people hat' and when it's over, take it off again.&lt;/p&gt;

&lt;p&gt;If a situation is dire and nothing you do or say is respected or you are being treated horribly - consider leaving, but plan for it first. In my case I basically just upped and left (which in hindsight may not have been the greatest decision financially).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For some hope to you, I have now found a team - Stillio - who I've been working with for the past few months and I can honestly now say that the right people around you is just as important as what project you're working or what your salary is. The people at Stillio have truly been amazing and shown that the industry does have great teams. You just need to find them.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the fire for software lit
&lt;/h2&gt;

&lt;p&gt;This sounds a little on the nose, almost like saying 'If you are sad, just be happy 🙄'.&lt;/p&gt;

&lt;p&gt;The way I re-kindled the fire is to keep learning. This is what brought us into software in the first place and I think a lot of us forget that. There's a strange thing about motivation - where you think you need to find it first, before taking action. I found the reverse - that taking action towards learning even if there's little motivation, seems to bring that motivation back and suddenly work becomes exciting again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look after yourself in other areas
&lt;/h2&gt;

&lt;p&gt;Finally, there is no discounting health and wellness. Software can rob us of time and if we're not careful we'll start replacing time for nutrition, exercise, sleep and family with more hours in front of the PC, because we just HAVE to fix the bug or meet the deadline.&lt;/p&gt;

&lt;p&gt;These very things that we find unimportant in that moment will help us be better developers in the long run. Build a schedule that suites you that incorporates these elements. Remember that small changes compound over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;endOfTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iDoJustALittleBit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;itAddsUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iDoNothing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// I will&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you're struggling with this reality of burnout in software development. Self-analysis, planning difficult interactions with difficult people (Or planning to leave if the situation calls for it), keeping the student mindset alive and revisiting healthier habits in other areas could help you like it did me.&lt;/p&gt;

&lt;p&gt;And as the adage goes: 'This too shall pass.' Be kind.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post mentions Stillio, with whom I am affiliated. Stillio is a screenshot automation SaaS for brand management, SEO tracking and more. Check out what I'm working on by visiting them at &lt;a href="https://www.stillio.com" rel="noopener noreferrer"&gt;https://www.stillio.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>burnout</category>
      <category>software</category>
      <category>life</category>
    </item>
  </channel>
</rss>
