<?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: Mike CK</title>
    <description>The latest articles on DEV Community by Mike CK (@mikeck).</description>
    <link>https://dev.to/mikeck</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%2F139766%2Fe8dddc7f-3028-4e9c-802e-83a1d8ccbcfa.jpg</url>
      <title>DEV Community: Mike CK</title>
      <link>https://dev.to/mikeck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mikeck"/>
    <language>en</language>
    <item>
      <title>Installing PocketBase on a VPS Complete with SSL - Let's Enrypt</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Mon, 26 Feb 2024 07:42:00 +0000</pubDate>
      <link>https://dev.to/mikeck/installing-pocketbase-on-a-vps-complete-with-ssl-lets-enrypt-3nd3</link>
      <guid>https://dev.to/mikeck/installing-pocketbase-on-a-vps-complete-with-ssl-lets-enrypt-3nd3</guid>
      <description>&lt;p&gt;PocketBase is promising and might fully win me over. I have been playing with it on a few small projects and I appreciate it's strengths now over SupaBase. I have nothing against SupaBase by the way, it just doesn't make sense for a small project to go with it given how bulky it is to host. The best bet is if you go with their managed option, in which case, you didn't leave Firebase, did yuh?&lt;/p&gt;

&lt;p&gt;I am making this short tutorial for my future use, I find it easier to log onto my blog when &lt;a href="https://mikeck.elevatika.com/posts/deploy-host-django-any-vps-docker-caddy-docker-compose/"&gt;deploying a docker app&lt;/a&gt;, and so if PocketBase becomes my fully go-to backend for the next few years, I might end up reading this article more times than you. (I am terrified AI makes blogging silly these days. Why would you come read this article when you can go to ChatGPT and ask "How to self-host PocketBase on a VPS?"). I have no idea why you are here, you probably like it better when someone vouches for a given approach. Well, I vouch for this installation process. Enough disclaimers.&lt;/p&gt;

&lt;h2&gt;
  
  
  First things First, Deploy a VPS
&lt;/h2&gt;

&lt;p&gt;Need I mention that you need a working VPS for this? Pick any Linux server. I recommend DigitalOcean. However, I am becoming a fan of OVHCloud, though the process of deploying a server over there is not as straightforward as you'd expect. In fact, with OVHCloud, you'll have fun if you prefer setting up most things. For example, by default, the ssh login to your VPS is username and password 🩻That is dangerous. So I went ahead and disabled that after adding an ssh key. By the way, to add an SSH key, you only need to do:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ssh-copy-id {USER}@{SERVER_IP}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And then, as usual, ssh to the server and let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and Running PocketBase on a VPS with SSL (Let's Encrypt)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download PocketBase to your Linux VPS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;curl -L -o pocketbase.zip https://github.com/pocketbase/pocketbase/releases/download/v0.21.3/pocketbase_0.21.3_linux_amd64.zip&lt;/code&gt; - This downloads the specified version of PocketBase. To get the latest version, go to &lt;a href="https://pocketbase.io/docs/"&gt;https://pocketbase.io/docs/&lt;/a&gt;, right click on the Linux download option and copy the link. In Curl, the &lt;code&gt;-L&lt;/code&gt;option tells curl to follow redirects, while the &lt;code&gt;-o&lt;/code&gt; option specifies the output file.&lt;/p&gt;

&lt;p&gt;If you run the command at the home of your VPS user, the file will be downloaded to &lt;code&gt;/home/{user}&lt;/code&gt; so in my case, since I am using &lt;code&gt;ubuntu&lt;/code&gt; user, the download goes to &lt;code&gt;/home/ubuntu&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Next, unzip the file to a folder called &lt;code&gt;pb&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;unzip pocketbase.zip -d pb&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make PocketBase executable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;chmod +x pb/pocketbase&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allow PocketBase to access lower ports (so it can run on port 80 and 443 if you are using a domain)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo setcap 'cap_net_bind_service=+ep' /home/ubuntu/pb/pocketbase&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a service to manage PocketBase&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo vim /lib/systemd/system/pocketbase.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add the following to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description = pocketbase

[Service]
Type           = simple
User           = ubuntu
Group          = ubuntu
LimitNOFILE    = 4096
Restart        = always
RestartSec     = 5s
StandardOutput = append:/home/ubuntu/pb/errors.log
StandardError  = append:/home/ubuntu/pb/errors.log
ExecStart      = /home/ubuntu/pb/pocketbase serve {YOUR_DOMAIN_OR_SUB_DOMAIN_HERE}

[Install]
WantedBy = multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the file paths and the user. Also, include your subdomain or domain. (Which by now you have pointed to the VPS I suppose). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NB: Running PocketBase with a domain/sub-domain allows it to assign SSL cert automatically and binds to 80 and 443 meaning you are able to access it without a proxy server. The people behind it are thoughtful like that. Can you imagine not needing a proxy server? Plus automated SSL?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Reload systemctl and include the service&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl enable pocketbase.service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl start pocketbase&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bonus
Check the logs of PocketBase. You can also check for errors by running &lt;code&gt;cat ~/pb/errors.log&lt;/code&gt;  if you followed the setup above. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sudo journalctl -u pocketbase&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is it! you should now be able to access the PocketBase Admin by going to &lt;code&gt;yourdomain.tld/_/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hugo, Tailwind CSS and daisyUI starter</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Tue, 02 Jan 2024 10:02:58 +0000</pubDate>
      <link>https://dev.to/mikeck/hugo-tailwind-css-and-daisyui-starter-3h5b</link>
      <guid>https://dev.to/mikeck/hugo-tailwind-css-and-daisyui-starter-3h5b</guid>
      <description>&lt;p&gt;I love &lt;a href="https://gohugo.io/installation/"&gt;Hugo&lt;/a&gt;. I use Hugo for basically all websites that are static. I came across &lt;a href="https://daisyui.com/docs/install/"&gt;daisyUI&lt;/a&gt; recently and could not believe how easy it was to integrate to a project that already supports &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;. It comes with tones of themes too. &lt;/p&gt;

&lt;p&gt;The starter uses &lt;a href="https://www.npmjs.com/package/concurrently"&gt;concurrently&lt;/a&gt; to run both &lt;a href="https://gohugo.io/installation/"&gt;Hugo&lt;/a&gt; and &lt;a href="https://tailwindcss.com/docs/installation"&gt;Tailwind CLI&lt;/a&gt; in one terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;You need Hugo installed and available. Test &lt;a href="https://gohugo.io/installation/"&gt;Hugo&lt;/a&gt; works by running &lt;code&gt;hugo version&lt;/code&gt;&lt;br&gt;
You also need &lt;a href="https://nodejs.org/en"&gt;Node.js and NPM&lt;/a&gt; installed. Confirm these are installed by running &lt;code&gt;node --version &amp;amp;&amp;amp; npm --version&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repo: &lt;a href="https://github.com/CkCreative/hugo-tailwind-daisyui"&gt;https://github.com/CkCreative/hugo-tailwind-daisyui&lt;/a&gt; and run &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm start&lt;/code&gt; and visit &lt;code&gt;localhost:1313&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enjoy 🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Binance Spot Trading Bot - Nodejs - JavaScript</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Wed, 31 Mar 2021 03:35:05 +0000</pubDate>
      <link>https://dev.to/mikeck/binance-spot-trading-bot-nodejs-javascript-2be1</link>
      <guid>https://dev.to/mikeck/binance-spot-trading-bot-nodejs-javascript-2be1</guid>
      <description>&lt;p&gt;Say hello to a Binance Spot Trading bot. Let me know what you think in the comments. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/CkCreative/binance-spot-trading-bot"&gt;https://github.com/CkCreative/binance-spot-trading-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>trading</category>
      <category>javascript</category>
      <category>node</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Self Hosted Hasura + GoTrue = ❤️</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Sat, 20 Feb 2021 04:07:54 +0000</pubDate>
      <link>https://dev.to/mikeck/self-hosted-hasura-gotrue-2260</link>
      <guid>https://dev.to/mikeck/self-hosted-hasura-gotrue-2260</guid>
      <description>&lt;h2&gt;
  
  
  Hasura + GoTrue = ❤️
&lt;/h2&gt;

&lt;p&gt;This is a slightly extended version of &lt;a href="https://github.com/netlify/gotrue"&gt;Netlify's GoTrue&lt;/a&gt;. It includes a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file to deploy it together with &lt;a href="https://hasura.io/docs/1.0/graphql/core/index.html"&gt;Hasura&lt;/a&gt;. This set up uses two databases, &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; for &lt;a href="https://hasura.io/docs/1.0/graphql/core/index.html"&gt;Hasura&lt;/a&gt; and &lt;a href="https://mariadb.org/"&gt;MariaDB&lt;/a&gt; for &lt;a href="https://github.com/netlify/gotrue"&gt;GoTrue&lt;/a&gt;. Have fun!&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Setting up &lt;a href="https://hasura.io/docs/1.0/graphql/core/index.html"&gt;Hasura&lt;/a&gt; is very easy. However, you immediately get disappointed that you don't get authentication out of the box. You have to read through a lot of tutorial blogs just to end up using paid options or Firebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/netlify/gotrue"&gt;GoTrue&lt;/a&gt; is a simple yet solid authentication and user management tool. It is very light and straightforward. You can sign up users, verify them and also help them reset their passwords. The means of authentication is JWT, meaning that after signing up, a client sends a username and password to the system and they get an &lt;code&gt;access_token&lt;/code&gt; and a &lt;code&gt;refresh_token&lt;/code&gt;. This way, the client can be able to attach the token each time it makes a request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hasura.io/docs/1.0/graphql/core/index.html"&gt;Hasura&lt;/a&gt; is a very neat GraphQL engine built on top of PostgreSQL using Elixir. This gives you highly scalable GraphQL APIs that you can consume using web, mobile and desktop applications written in various programming languages. Hasura is nice in that it gives you room for multiple authentication and authorization options including JWT and WebHooks.&lt;/p&gt;

&lt;p&gt;When you send a JWT to hasura in form of &lt;code&gt;Authorization: Bearer token&lt;/code&gt;, Hasura engine checks the signature of the token using a preconfigured secret. Therefore, it doesn't have to send the token to the issuer from the server side to confirm its authenticity, Hasura simply checks the signature and, if it checks out, proceeds to authorize the request according to the claims available inside the token.&lt;/p&gt;

&lt;p&gt;Which brings us to the claims and GoTrue. Hasura requires some custom claims which are not available by default inside the tokens generated by GoTrue. So, I simply took GoTrue source code and extended the &lt;code&gt;token.go&lt;/code&gt; file to include Hasura claims inside the generated token.&lt;/p&gt;

&lt;p&gt;This means the rest of GoTrue code works as expected and will be easy to update in future. The added code is less than 10 lines, and I am new to Go, so perhaps there is an even shorter way of adding the feature.&lt;/p&gt;

&lt;p&gt;I also included the deployment file I use, this should work out of the box, but there is a catch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Clone this repositoy, modify the &lt;code&gt;.env&lt;/code&gt; file to include your preferred details for passwords, domain and smtp settings for emails then run &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;docker-compose.yaml&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:12&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pgpassword&lt;/span&gt;

  &lt;span class="na"&gt;hasura&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hasura/graphql-engine:v1.3.3&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:pgpassword@postgres:5432/postgres&lt;/span&gt;
      &lt;span class="c1"&gt;## enable the console served by server&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ENABLE_CONSOLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt; &lt;span class="c1"&gt;# set to "false" to disable console&lt;/span&gt;
      &lt;span class="c1"&gt;## enable debugging mode. It is recommended to disable this in production&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_DEV_MODE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_UNAUTHORIZED_ROLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_SERVER_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ENABLED_LOG_TYPES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;startup, http-log, webhook-log, websocket-log, query-log&lt;/span&gt;
      &lt;span class="c1"&gt;## uncomment next line to set an admin secret&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_ADMIN_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AdminSecretHere&lt;/span&gt;
      &lt;span class="na"&gt;HASURA_GRAPHQL_JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"type":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"HS256",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"key":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"changethismorethan32characterstring"}'&lt;/span&gt;

  &lt;span class="na"&gt;caddy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;abiosoft/caddy&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ACME_AGREE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./Caddyfile:/etc/Caddyfile&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;caddy_certs:/root/.caddy&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mariadb:10&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbpassword&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mariadb:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;3306&lt;/span&gt;
  &lt;span class="na"&gt;gotrue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gotrue&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PORT=${GOTRUE_PORT}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DATABASE_URL=${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(db:3306)/${MYSQL_DATABASE}?parseTime=true&amp;amp;multiStatements=true"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${GOTRUE_PORT}:${GOTRUE_PORT}&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;caddy_certs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mariadb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Caddy server &lt;code&gt;Caddyfile&lt;/code&gt; looks like following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hasura.example.com {
  bind {$ADDRESS}
  proxy / hasura:5000 {
    transparent
  }
  tls email@example.com
}

gotrue.example.com {
  bind {$ADDRESS}
  proxy / gotrue:9999 {
    transparent
  }
  tls email@example.com
}

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

&lt;/div&gt;



&lt;p&gt;_Modify the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file and the &lt;code&gt;Caddyfile&lt;/code&gt; to suite your set up.&lt;/p&gt;

&lt;p&gt;You will then need to manually run migrations inside the running &lt;code&gt;gotrue&lt;/code&gt; container by doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;gotrue /bin/ash
gotrue migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command brings up the shell of the &lt;code&gt;gotrue&lt;/code&gt; container that is running. The &lt;code&gt;gotrue&lt;/code&gt; image is built from alpine, therefore the default shell is &lt;code&gt;ash&lt;/code&gt;, don't bother looking for &lt;code&gt;zsh&lt;/code&gt; or &lt;code&gt;bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second command is what you will run inside the container, this will run the migrations and you are now ready to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sign Up a User
&lt;/h3&gt;

&lt;p&gt;Curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s1"&gt;'https://gotrue.example.com/signup'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
  "email": "user@email.com",
  "password": "password"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript:&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;var&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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;email@example.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="s2"&gt;password&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;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&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;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;follow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://gotrue.example.com/signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get a Token for This User
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s1"&gt;'https://gotrue.example.com/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/x-www-form-urlencoded'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=password'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'username=email@example.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'password=password'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;urlencoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grant_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;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&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;email@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&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;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&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;urlencoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;follow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://gotrue.example.com/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token_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;"bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;864000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aWF0IjoxNTE2MjM5-2kNw"&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;h3&gt;
  
  
  Using the Token in graphQL requests to Hasura
&lt;/h3&gt;

&lt;p&gt;Given the token above, when making a request to Hasura, add the access token provided for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/CkCreative/hasura_gotrue"&gt;https://github.com/CkCreative/hasura_gotrue&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hasura</category>
      <category>gotrue</category>
      <category>jwt</category>
      <category>authentication</category>
    </item>
    <item>
      <title>What Motivates You as a Developer</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Sat, 31 Oct 2020 06:10:37 +0000</pubDate>
      <link>https://dev.to/mikeck/what-motivates-you-as-a-developer-9n6</link>
      <guid>https://dev.to/mikeck/what-motivates-you-as-a-developer-9n6</guid>
      <description>&lt;p&gt;My days are like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High motivation and fast results&lt;/li&gt;
&lt;li&gt;No desire to open the computer at all&lt;/li&gt;
&lt;li&gt;Trading simpler projects for complicated ones&lt;/li&gt;
&lt;li&gt;Implementing end-to-end solutions without asking for a dime&lt;/li&gt;
&lt;li&gt;Rewriting systems from scratch because one thing doesn't work&lt;/li&gt;
&lt;li&gt;Mastering the difficult stuff&lt;/li&gt;
&lt;li&gt;Feeling payment is undeserved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Swinging between these states had confused me for a long time, until I decided to focus on the &lt;strong&gt;why&lt;/strong&gt; of doing anything at all. I mean, why am I even a developer? Can I do well in another field? Yes. Can I give up programming for good? Absolutely no.&lt;/p&gt;

&lt;p&gt;I do programming because there is enjoyment in the process, and there is satisfaction in the results. The more difficult the process, the more fulfilling the results. It's a feel-good journey.&lt;/p&gt;

&lt;p&gt;Oh so simple: enjoy learning, working and delivering results!&lt;/p&gt;

&lt;p&gt;Enjoy the frustrations you get when you are stuck. Have fun with your 1 week breaks between coding. Dance to the tunes of abandoned projects. Sleep soundly while procrastinating. It's all part of the journey.&lt;/p&gt;




&lt;p&gt;In December 2014, I made a simple website so I can start explaining tech to people. I was so certain it was my calling.&lt;/p&gt;

&lt;p&gt;Two years later, I abandoned it so I can push a design and development business. I made a few projects that excited me. In all of them, there were moments of great triumph and moments of absolute chaos. Amid these chaos, I would contemplate telling the client to give the project to someone else.&lt;/p&gt;

&lt;p&gt;Design projects too are frustrating. I would take a week, still do, sometimes to get inspiration on the most fitting logo design for a given company.&lt;/p&gt;




&lt;p&gt;This short story expresses one thing: we are motivated by good results and fulfilling/exciting experiences. &lt;/p&gt;

&lt;p&gt;Being a developer, just like any other profession, is a roller-coaster that doesn't end. It's okay to be wherever you are in the journey.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Authentication in Phoenix Framework Using Guardian - Boilerplate Code</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Mon, 29 Jun 2020 17:27:42 +0000</pubDate>
      <link>https://dev.to/mikeck/authentication-in-phoenix-framework-using-guardian-boilerplate-code-2b88</link>
      <guid>https://dev.to/mikeck/authentication-in-phoenix-framework-using-guardian-boilerplate-code-2b88</guid>
      <description>&lt;p&gt;As mentioned in the comments by &lt;a href="https://dev.to/wulymammoth"&gt;David&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Phoenix now has its own auth written by Jose Valim and Aaron Renner - github.com/aaronrenner/phx_gen_auth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Therefore the method below might be unnecessary..&lt;/p&gt;




&lt;p&gt;Here is the repo &lt;a href="https://github.com/CkCreative/phoenix_151_guardian/"&gt;Barebones Phoenix 1.5.1 Project Using Guardian for Authentication&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The protected route is &lt;code&gt;/products&lt;/code&gt;. All other routes are free to access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For some weird reason, the project won't compile under Windows since it requires Argon2_elixir (which requires native compilation that is not easy to set up).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get the project working, I used WSL from within VS Code and everything works 100%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Elixir and Its Tools in WSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
wget https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; erlang-solutions_2.0_all.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;esl-erlang
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;elixir
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;build-essential
mix archive.install hex phx_new 1.5.1
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; inotify-tools

curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bas
nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you need to access a PostgreSQL instance that you installed inside Windows, you can do:&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-client-common
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-client
psql &lt;span class="nt"&gt;-h&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-U&lt;/span&gt; postgres

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

&lt;/div&gt;



&lt;p&gt;To start your Phoenix server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install dependencies with &lt;code&gt;mix deps.get&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create and migrate your database with &lt;code&gt;mix ecto.setup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install Node.js dependencies with &lt;code&gt;cd assets &amp;amp;&amp;amp; npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start Phoenix endpoint with &lt;code&gt;mix phx.server&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can visit &lt;a href="http://localhost:4000"&gt;&lt;code&gt;localhost:4000&lt;/code&gt;&lt;/a&gt; from your browser.&lt;/p&gt;

&lt;p&gt;Ready to run in production? Please &lt;a href="https://hexdocs.pm/phoenix/deployment.html"&gt;check deployment guides&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official website: &lt;a href="https://www.phoenixframework.org/"&gt;https://www.phoenixframework.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guides: &lt;a href="https://hexdocs.pm/phoenix/overview.html"&gt;https://hexdocs.pm/phoenix/overview.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://hexdocs.pm/phoenix"&gt;https://hexdocs.pm/phoenix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Forum: &lt;a href="https://elixirforum.com/c/phoenix-forum"&gt;https://elixirforum.com/c/phoenix-forum&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/phoenixframework/phoenix"&gt;https://github.com/phoenixframework/phoenix&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Hugo PWA and Self Hosted Comments</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Fri, 19 Jun 2020 05:53:07 +0000</pubDate>
      <link>https://dev.to/mikeck/hugo-pwa-and-self-hosted-comments-5d4p</link>
      <guid>https://dev.to/mikeck/hugo-pwa-and-self-hosted-comments-5d4p</guid>
      <description>&lt;h2&gt;
  
  
  Summary Process
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make a Hugo website&lt;/li&gt;
&lt;li&gt;Add PWA configs&lt;/li&gt;
&lt;li&gt;Get comments as JSON and add to data folder so Hugo can use during build time&lt;/li&gt;
&lt;li&gt;NetlifyCMS saves images in one folder, at build time, copy the images to content folder and then process them with Hugo&lt;/li&gt;
&lt;li&gt;Enjoy lightning speeds.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;This article is long overdue. My brain right now is working at 200% trying to put together the development process involved - what I should have simplified using notes as I made the website. I will not make that mistake again in future.&lt;/p&gt;

&lt;p&gt;Let me start.&lt;/p&gt;

&lt;p&gt;&lt;a href="//www.mbabaziafrica.com"&gt;Heart and Soul&lt;/a&gt; launched in 2019 powered by &lt;a href="https://www.gatsbyjs.org/"&gt;GatsbyJS&lt;/a&gt;. I loved Gatsby back then. I still do, and it runs &lt;a href="https://elevatika.com"&gt;Elevatika&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PWA and image processing attracted me to Gatsby. It has powerful plugins on that front.&lt;/p&gt;

&lt;p&gt;However, I am not comfortable with the huge JSON file that is sent to the client just so routes can be hydrated.&lt;/p&gt;

&lt;p&gt;As you launch your website, you won't notice it at first since you have just a few pages. And so when you visit Google Pagespeed and run your tests, the results will be excellent.&lt;/p&gt;

&lt;p&gt;Wait until you have a few hundred articles. The site starts to drag. You begin to notice nonresponsiveness very fast. If you are on a fast connection, you will not notice. However, not all your visitors will be from the top cities in the world.&lt;/p&gt;

&lt;p&gt;Another problem I noticed was by design. A website whose navigation is powered by JavaScript totally breaks when subjected to mobile browsers like Opera Mini.&lt;/p&gt;

&lt;p&gt;It is not easy to tell people not to use Opera Mini when other websites just work. Breaks trust kind of.&lt;/p&gt;

&lt;p&gt;Then the comments. The comments weren't loading as expected. Disqus can be slow. I use it on this blog, but just because comments are just not that important here. Users were complaining they just don't know what to do. How to comment that is. I recall Disqus UX used to be better some years ago. Not anymore. I had to find an alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome to Version 2.0
&lt;/h2&gt;

&lt;p&gt;Gatsby is beautiful. How can I replicate the best features in Hugo? Almost everything I needed is there already, just that I needed some research. Let's see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image Processing
&lt;/h3&gt;

&lt;p&gt;Hugo allows you to process images if they are page resources "bundled" together with the content. That means that you need to have the image within the content directory, not the static directory.&lt;/p&gt;

&lt;p&gt;NetlifyCMS by design saves images in a separate directory from your markdown files. GatsbyJS did not care about this at all. It was able to process images no matter the source.&lt;/p&gt;

&lt;p&gt;I could have just decided to put all the images in the content directory and Hugo would process them. But that is not ideal given that there are other images linked within the markdown files and those would be lost as NetlifyCMS uses the relative directory it uploaded the files to on links.&lt;/p&gt;

&lt;p&gt;I needed a different approach.&lt;/p&gt;

&lt;p&gt;I decided to write a script to copy the files to the content directory at build time. That way the images used in featured section would be processed and linked. As for those linked within the body of the markdown files, they would stay unprocessed. They are not that many though, only less than ten posts have the images in the body. The rest have only the featured image.&lt;/p&gt;

&lt;p&gt;As you can probably guess, my approach means that if the content folder is big enough, it will be a problem since we are duplicating it. However, you can task a CI to do the job for you and you won't have any problem.&lt;/p&gt;

&lt;p&gt;Some comments online suggest you can use symlinks. I didn't try.&lt;/p&gt;

&lt;h3&gt;
  
  
  The PWA
&lt;/h3&gt;

&lt;p&gt;I found &lt;a href="https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/"&gt;https://www.freecodecamp.org/news/build-a-pwa-from-scratch-with-html-css-and-javascript/&lt;/a&gt; and it saved me a lot of time. Apparently, adding PWA is not that difficult. You just have to add a few JS files, a manifest.json file and then the necessary images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self Host Comments for a JAMstack Website
&lt;/h3&gt;

&lt;p&gt;I believe HTML is capable enough and does not need much help submitting form data. I wanted the comment forms to be accessible even without JavaScript. Anyone should be able to submit without much trouble.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have been slowly breaking the web by giving JavaScript all the power.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The approach I used is neat in my opinion.&lt;/p&gt;

&lt;p&gt;I made a simple &lt;a href="http://expressjs.com/"&gt;express&lt;/a&gt; API that receives the form post. No JS form submission. The API uses SQLite to store the comments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, I use &lt;a href="https://upcloud.com/signup/?promo=K65ZN5"&gt;Upcloud&lt;/a&gt; to host my Node.js projects using docker. I use Caddy server. You can click on &lt;a href="https://upcloud.com/signup/?promo=K65ZN5"&gt;this referral link&lt;/a&gt; to get $25 to try it out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I could have also made a Lambda function or a Firebase Cloud Function for the same and use Firebase to store the comments. And I encourage anyone to go that route to avoid managing the API server. It's free.&lt;/p&gt;

&lt;p&gt;When comments are submitted, they are queued for review. They appear after they have been approved.&lt;/p&gt;

&lt;p&gt;The approval process is also simple, I made a simple way to load all the pending comments and the admin can simply change the status to approved.&lt;/p&gt;

&lt;p&gt;The comments also have a hierarchy. Just a single level hierarchy though, for simplicity. So, you can reply to the comments but you cannot reply to the replies.&lt;/p&gt;

&lt;p&gt;I made this possible by letting the comments have a parent. The following is a look at one of the comment objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&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="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment&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="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parent&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="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&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="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At build time, I download the comments using a node.js script that looks like so:&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;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="s1"&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="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;✔ Requesting comments from Elevatika Cloud...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&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="s1"&gt;https://example.com/comments/API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;//get json&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;comments&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✔ Number of comments: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comments&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="c1"&gt;//not important&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;✔ Writing data...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./data/comments_json.json&lt;/span&gt;&lt;span class="dl"&gt;'&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;comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&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;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="c1"&gt;//pretty json file&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is a dirty logic I use to display the comments (I don't know much Golang, so excuse my logic):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments_json&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;findRE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;//this matches the current url i.e current post&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)}}&lt;/span&gt; &lt;span class="c"&gt;//if it is an original comment, not reply&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_single_comment"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_comment_name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_comment_content"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;safeHTML&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_hide_form"&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;fieldset&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="c"&gt;//the reply box with inputs&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;fieldset&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;form&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;range&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;comments&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="n"&gt;eq&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;//display respective replies&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_reply"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_comment_name"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elev_comment_content"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;safeHTML&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Advantage of Adding Comments at Build Time
&lt;/h4&gt;

&lt;p&gt;Your commenters are mentioning important keywords that you should be leveraging for SEO. Every time you use a JavaScript embedded comment section like Disqus, I am one of them, you are missing out.&lt;/p&gt;

&lt;p&gt;The above Hugo code makes sure that at build time, the comments are added to the HTML therefore being like the good old serverside wordpress comments. Which are awesome.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;netlify.toml&lt;/code&gt; file allows you to specify custom build command. Therefore, my custom build command calls a build script that copies the images and then gets the comments from the API then invokes the hugo build command.&lt;/p&gt;

&lt;p&gt;The netlify.toml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build]&lt;/span&gt;
  &lt;span class="py"&gt;publish&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"public"&lt;/span&gt;
  &lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bash build.sh"&lt;/span&gt;

&lt;span class="nn"&gt;[build.environment]&lt;/span&gt;
  &lt;span class="py"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.71.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bash script:&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;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-Rf&lt;/span&gt; ./static/assets ./content/assets
node index.js
hugo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Google PageSpeed Insight Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kA09UNgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https:/mikeck.elevatika.com/images/heart_and_soul_google_page_speed.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kA09UNgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https:/mikeck.elevatika.com/images/heart_and_soul_google_page_speed.JPG" alt="The Google Pagespeed Insight Results" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't you love those speeds? The speed jumped from 79 to 98 and sometimes it is even 100%!&lt;/p&gt;




&lt;p&gt;Feel free to leave any questions below.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>pwa</category>
      <category>webdev</category>
    </item>
    <item>
      <title>10 Important Web Technologies and Platforms for Modern Companies</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Sat, 25 Jan 2020 05:24:21 +0000</pubDate>
      <link>https://dev.to/mikeck/10-important-web-technologies-and-platforms-for-modern-companies-8hm</link>
      <guid>https://dev.to/mikeck/10-important-web-technologies-and-platforms-for-modern-companies-8hm</guid>
      <description>&lt;p&gt;You already know how much benefits your business will enjoy when you take it online. However, many at times, you will find yourself struggling with immense bills and technology stacks that are hard to manage. I have come up with this short list from my personal experience and working with clients from different industries. I think I will expand the list soon.&lt;/p&gt;

&lt;p&gt;I have used these technologies and platforms to deliver reliable business solutions at very attractive rates. These technologies are the reason products and services delivered by our company are reliable, and yet affordable.&lt;/p&gt;

&lt;h2&gt;
  
  
  JAMstack
&lt;/h2&gt;

&lt;p&gt;What is JAMstack? JAMstack stands for JavaScript, APIs, and Markup. It is a modern technology stack used to deliver super-fast websites. The resulting websites are also easy to find on Google due to excellent SEO as well as cheap to host because the resulting websites don't need a server to run.&lt;/p&gt;

&lt;p&gt;Read more about JAMstack in this excellent &lt;a href="https://www.netlify.com/whitepaper/"&gt;whitepaper on Netlify&lt;/a&gt;. It shows how some companies deliver 10x better web solutions to their visitors.&lt;/p&gt;

&lt;p&gt;JAMstack, in simple terms, refers to a website/app that is ready to be consumed by the browser and does not require any code to run on the server for it to function. However, if there is anything that needs to be updated, say you submit a form, that is when some code on the server can be invoked.&lt;/p&gt;

&lt;p&gt;Traditional websites, on the other hand, require that a server runs the code and spits out web pages for every request. I know some caching can help in most cases, but at the end of the day, a website is compiling a page when requested for the first time. Yet, for JAMstack, the website is static/ready to be displayed by the browser all the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlify
&lt;/h2&gt;

&lt;p&gt;I explained a little about JAMstack above and gave you the link to a whitepaper that &lt;a href="https://www.netlify.com"&gt;Netlify&lt;/a&gt; released. Well, the term JAMstack was coined by the founder of Netlify. So, it is really hard to recommend JAMstack to a client without mentioning Netlify.&lt;/p&gt;

&lt;p&gt;You see, Netlify allows you to host websites and web applications at a fraction of the cost of hosting traditional web applications. You can only host JAMstack solutions on Netlify.&lt;/p&gt;

&lt;p&gt;The reason Netlify is able to offer very cheap hosting packages and even free as you start, should not be surprising since I already mentioned that JAMstack websites are very cheap to host.&lt;/p&gt;

&lt;p&gt;Netlify is excellent for new companies that are starting as they have a generous free plan. At the same time, existing companies can benefit by gradually migrating parts of their existing web solutions to JAMstack and hosting them for free over there as they analyze and observe to establish whether to migrate fully or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github, Bitbucket or Gitlab
&lt;/h2&gt;

&lt;p&gt;Git is not just a good technology for coders. &lt;a href="https://github.com"&gt;Github&lt;/a&gt;, &lt;a href="https://bitbucket.org"&gt;Bitbucket&lt;/a&gt; and &lt;a href="https://gitlab.com"&gt;Gitlab&lt;/a&gt; are excellent collaboration tools that you can use alongside modern web technologies and platforms especially Netlify and the JAMstack.&lt;/p&gt;

&lt;p&gt;If you know how Git works, you already know the benefits. But still, for a JAMstack website hosted by Netlify, every time you push changes to Github, Bitbucket or Gitlab, Netlify will start publishing these changes instantly such that if this were a new article, it will be visible to the world within a few seconds.&lt;/p&gt;

&lt;p&gt;These three platforms are also excellent for backing up your code, your articles, and even documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;It is working on my computer, why does it fail on the web?&lt;/p&gt;

&lt;p&gt;This is a question that rarely comes up when you use &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;. Docker makes building and publishing web applications simple and reliable.&lt;/p&gt;

&lt;p&gt;Also, if you are thinking of scaling your application, Docker has great tools that will allow you to serve millions of users across the world.&lt;/p&gt;

&lt;p&gt;Are you scared of server administration? Docker will help ease the burden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital Ocean
&lt;/h2&gt;

&lt;p&gt;Docker gives you the ability to assure your application will run as good on the server as it does on your computer. &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt;, on the other hand, gives you the platform to host Docker containers at the cheapest rates around.&lt;/p&gt;

&lt;p&gt;Also, it will give you a chance to start deploying multiple containers when your user base multiplies. Therefore, you can host simple applications that serve a small user base or enterprise applications that serve many many users.&lt;/p&gt;

&lt;p&gt;When I say Digital Ocean offers cheap hosting, I mean as cheap as 5 USD per month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hasura
&lt;/h2&gt;

&lt;p&gt;Do you feel database administration is difficult? Do you want a simple way to access your data? Do you want a realtime database that you can connect to your application with a few magical lines of code?&lt;/p&gt;

&lt;p&gt;Meet &lt;a href="https://www.hasura.io"&gt;Hasura&lt;/a&gt;. Hasura is a GraphQL database engine. What this means is that, instead of rolling out your database and then writing a thousand lines of code to create an API to interact with the database, Hasura gives you excellent APIs for free. Also, the engine is highly optimized. So much that you can support millions of database reads and writes without any problem.&lt;/p&gt;

&lt;p&gt;You can use Hasura to make:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Online chat applications&lt;/li&gt;
&lt;li&gt;Mobile chat applications&lt;/li&gt;
&lt;li&gt;Realtime data applications like stock tracking apps&lt;/li&gt;
&lt;li&gt;Online communities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read more about GraphQL from &lt;a href="https://www.apollographql.com/docs/tutorial/introduction/"&gt;Apollo GraphQL guides&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Functions (Cloud Functions or Lambda Functions)
&lt;/h2&gt;

&lt;p&gt;Sometimes all you need is a function that does one specific thing when a given event occurs. For example, you want to send out an email alert whenever your database is updated.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://www.hasura.io"&gt;Hasura&lt;/a&gt;, you get the ability to use webhooks whenever data changes.&lt;/p&gt;

&lt;p&gt;Therefore, instead of having to create entire applications to receive such web hooks, you can simply create a serverless function, which is easier to host and is fast.&lt;/p&gt;

&lt;p&gt;A serverless function is a simple piece of code that runs when a given URL requested. All you do is write the function and never have to worry about the underlying infrastructure or perform server administration.&lt;/p&gt;

&lt;p&gt;Google Cloud Platform, Firebase, Amazon, Netlify, and Microsoft Azure all offer a way for you to host serverless functions, mostly in JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hugo and Gatsbyjs
&lt;/h2&gt;

&lt;p&gt;The first web technology I introduced is JAMstack. Well, essentially, JAMstack gives you a static website. A static website is generated using various tools. Among these tools are &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; and &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsbyjs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several other static website generators but whenever I make a website for a client, I always choose either Hugo or Gatsbyjs. The two are very beneficial.&lt;/p&gt;

&lt;p&gt;Hugo is blazing fast. Like you can make a website with hundreds of pages, e.g a large blog, and it will finish building the website within just a few seconds. So, by default, I always go with Hugo.&lt;/p&gt;

&lt;p&gt;Gatsbyjs is excellent for PWAs. Progressive web apps are the real deal at the moment. The reason for this popularity in PWAs is that you can install a PWA on a phone just like any other app you can download from Google Play Store. Oh, and you can even package a PWA as an APK and host it on Google Play Store.&lt;/p&gt;

&lt;p&gt;So, effectively, choosing to go the PWA route means getting two solutions in one implementation. Whenever I do need a PWA, I can trade the speed I get from Hugo for the excellent PWA support that I get from Gatsbyjs.&lt;/p&gt;

&lt;h2&gt;
  
  
  CircleCI
&lt;/h2&gt;

&lt;p&gt;The subtitle here should ideally be &lt;code&gt;continuous integration and continuous deployment&lt;/code&gt; or what is popularly known as CI/CD.&lt;/p&gt;

&lt;p&gt;Well, since I met &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt;, I have never desired to change to any other provider.&lt;/p&gt;

&lt;p&gt;Why would you need a CI/CD provider in the first place? Let me give you an example in form of a story. Two stories.&lt;/p&gt;

&lt;p&gt;A few months ago, I made a website for a client. It was a static website. I used Hugo.&lt;/p&gt;

&lt;p&gt;In the past, I had always relied on Github + Netlify, where I would push the code to Github and Netlify automatically builds the website and redeploys it. It had always been my default way of making the websites.&lt;/p&gt;

&lt;p&gt;However, in this case, the client already had an FTP server. So, I had to manually upload the changes using Cyberduck every time I make changes and want to update the website (Cyberduck is a very nice FTP server manager. It does many other things too, but its excellent for FTP). After a few tedious manual rounds, I decided to try CircleCI.&lt;/p&gt;

&lt;p&gt;I uploaded the code to Github and then connected the repo to CircleCI. With just a few lines in the config, CircleCI was able to build the website and publish the changes to the FTP server within a minute each time I pushed changes to Github.&lt;/p&gt;

&lt;p&gt;Another instance was when I wanted to make changes to a project I made a few months ago. I only had the code backed up on Github, but since I was using macOS yet I wanted to build for windows, it was a problem.&lt;/p&gt;

&lt;p&gt;I cloned the repo, made the changes and then pushed the updates back to Github. I had configured CircleCI to build for Windows. I then just waited for build to complete so I could download the Windows executable. All under 5 minutes.&lt;/p&gt;

&lt;p&gt;I explained the process in detail in the article &lt;strong&gt;&lt;a href="https://dev.to/elevatika/how-to-build-your-electron-project-using-circleci-1982"&gt;How to Build Your Electron Project Using CircleCI&lt;/a&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;div class="ltag__link__content"&gt;
    &lt;div class="missing"&gt;
      &lt;h2&gt;Article No Longer Available&lt;/h2&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Amazon S3 Object Storage
&lt;/h2&gt;

&lt;p&gt;Amazon S3 is a cheap storage for images, documents, videos, and many other types of files. It is so cheap, except if you have to download the objects every now and then. But still, many companies choose to go with it, mostly to store images they use on their web applications.&lt;/p&gt;

&lt;p&gt;You can also use it as a backup. Cheap backup!&lt;/p&gt;

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

&lt;p&gt;I hope you found this article insightful. Generally, I never stop evaluating web technologies and platforms to hit the sweet spot when it comes to value for money. Feel free to give me feedback via twitter &lt;code&gt;@elevatika&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I will update this article as I keep exploring.&lt;/p&gt;

</description>
      <category>web</category>
      <category>business</category>
      <category>technology</category>
    </item>
    <item>
      <title>Calculating the Factorial of 10_000 - Elixir</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Fri, 24 Jan 2020 09:25:34 +0000</pubDate>
      <link>https://dev.to/mikeck/calculating-the-factorial-of-10-000-117c</link>
      <guid>https://dev.to/mikeck/calculating-the-factorial-of-10-000-117c</guid>
      <description>&lt;p&gt;Today, while going through an Elixir guide, I realized that Integers have an arbitrarily large size. Like really huge! And so, the best way to test that out was to go ahead and calculate the factorial of 1000. It worked!!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NB: Even Google Search cant calculate factorial of 1000. They should try Elixir :D.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_NGTYT0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/dpmkzf6htez4glyh5s0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y_NGTYT0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/dpmkzf6htez4glyh5s0g.png" alt="Google can't manage it, lol!" width="698" height="321"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is how to calculate factorial of a number in Elixir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Factorial&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;fact&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="k"&gt;do&lt;/span&gt; &lt;span class="c1"&gt;# Define the function&lt;/span&gt;
        &lt;span class="c1"&gt;# If the number is zero or 1, return 1. Else, multiply the number by the result of the &lt;/span&gt;
        &lt;span class="c1"&gt;# next recursive call.&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;n&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;Factorial&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"enter number: "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="c1"&gt;# Get an interger input from the console.&lt;/span&gt;
&lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Factorial&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fact&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="c1"&gt;# Call the fact function inside the Factorial module and pass the number&lt;/span&gt;
&lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Answer is: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Print the number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then got excited and gave it 10,000 and to my surprise, it started printing out a veeeeery large number:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
mikeck@Mikes-MacBook examples % elixir factorial.exs &lt;br&gt;
enter number: 10000&lt;br&gt;
Answer is: 28462596809170545189064132121198688901480514017027992307941799942744113400037644437729907867577847758158840621423175288300423399401535187390524211613827161748198241998275924182892597878981242531205946599625986706560161572036032397926328736717055741975962099479720346153698119897092611277500484198845410475544642442136573303076703628825803548967461117097369578603670191071512730587281041158640561281165385325968425825995584688146430425589836649317059251717204276597407446133400054194052462303436869154059404066227828248371512038322178644627183822923899638992827221879702459387693803094627332292570555459690027875282242544348021127559019169425429028916907219097083690539873747452483372899521802363282741217040268086769210451555840567172555372015852132829034279989818449313610640381489304499621599999359670892980190336998484404665419236258424947163178961192041233108268651071354516845540936033009607210346944377982349430780626069422302681885227592057029230843126188497606560742586279448827155956831533440534425446648416894580425709461673613187605234982286326452921529423479870603344290737158688499178932580691483168854251956006172372636323974420786924642956012306288720122652952964091508301336630982733806353972901506581822574295475894399765113865541208125788683704239208764484761569001264889271590706306409661628038784044485191643790807186112370622133415415065991843875961023926713276546986163657706626438638029848051952769536195259240930908614471907390768585755934786981720734372093104825475628567777694081564074962275254993384112809289637516990219870492405617531786346939798024619737079041868329931016554150742308393176878366923694849025999607729684293977427536263119825416681531891763234839190821000147178932184227805135181734921901146246875769835373441456013122615221391178759688367364087207937002992038279198038702372078039140312368997608152840306051116709484722224870389199993442071395836983063962232079115624044250808919914319837120445598344047556759489212101498152454543594285414390843564419984224855478532163624030098442855331829253154206551237079705816393460296247697010388742206441536626733715428700789122749340684336442889847100840641600093623935261248037975293343928764398316390312776450722479267851700826669598389526150759007349215197592659192708873202594066382118801988854748266048342256457705743973122259700671936061763513579529821794290797705327283267501488024443528681645026165662837546519006171873442260438919298506071515390031106684727360135816706437861756757439184376479658136100599638689552334648781746143243573224864326798481981458432703035895508420534788493364582482592033288089025782388233265770205248970937047210214248413342465268206806732314214483854074182139621846870108359582946965235632764870475718351616879235068366271743711915723361143070121120767608697851559721846485985918643641716850899625516820910793570231118518174775010804622585521314764897490660752877082897667514951009682329689732000622392888056658036140311285465929084078033974900664953205873164948093883816198658850827382468034897864757116679890423568018303504133875731972630897909435710687797301633918087868474943633533893373586906405848417828065196275826434429258058422212947649402948622670761832988229004072390403733168207417413251656688443079339447019208905620788387585342512820957359307018197708340163817638278562539516825426644614941044711579533262372815468794080423718587423026200264221822694188626212107297776657401018376182280136857586442185863011539843712299107010094061929413223202773193959467006713695377097897778118288242442920864816134179562017471831609687661043140497958198236445807368209404022211181530051433387076607063149616107771117448059552764348333385744040212757031851527298377435921878558552795591028664457917362007221858143309977294778923720717942857756271300923982397921957581197264742642878266682353915687857271620146192244266266708400765665625807109474398740110772811669918806268726626565583345665007890309050656074633078027158530817691223772813510584527326591626219647620571434880215630815259005343721141000303039242866457207328473481712034168186328968865048287367933398443971236735084527340196309427697652684170174990756947982757825835229994315633322107439131550124459005324702680312912392297979030417587823398622373535054642646913502503951009239286585108682088070662734733200354995720397086488066040929854607006339409885836349865466136727880748764700702458790118046518296111277090609016152022111461543158317669957060974618085359390400067892878548827850938637353703904049412684618991272871562655001270833039950257879931705431882752659225814948950746639976007316927310831735883056612614782997663188070063044632429112260691931278881566221591523270457695867512821990938942686601963904489718918597472925310322480210543841044325828472830584297804162405108110326914001900568784396341502696521048920272140232160234898588827371428695339681755106287470907473718188014223487248498558198439094651708364368994306189650243288353279667190184527620551085707626204244509623323204744707831190434499351442625501701771017379551124746159471731862701565571266295855125077711738338208419705893367323724453280456537178514960308802580284067847809414641838659226652806867978843250660537943046250287105104929347267471267499892634627358167146935060495110340755404658170393481046758485625967767959768299409334026387269378365320912287718077451152622642548771835461108886360843272806227776643097283879056728618036048633464893371439415250259459652501520959536157977135595794965729775650902694428088479761276664847003619648906043761934694270444070215317943583831051404915462608728486678750541674146731648999356381312866931427616863537305634586626957894568275065810235950814888778955073939365341937365700848318504475682215444067599203138077073539978036339267334549549296668759922530893898086430606532961793164029612492673080638031873912596151131890359351266480818568366770286537742390746582390910955517179770580797789289752490230737801753142680363914244720257728891784950078117889336629750436804214668197824272980697579391742229456683185815676816288797870624531246651727622758295493421483658868919299587402095696000243560305289829866386892076992834030549710266514322306125231915131843876903823706205399206933943716880466429711476743564486375026847698148853105354063328845062012173302630676481322931561043551941761050712449024873277273112091945865137493190965162497691657553812198566432207978666300398938660238607357858114394715872800893374165033792965832618436073133327526023605115524227228447251463863269369763762510196714380125691227784428426999440829152215904694437282498658085205186576292992775508833128672638418713277780874446643875352644733562441139447628780974650683952982108174967958836452273344694873793471790710064978236466016680572034297929207446822322848665839522211446859572858403863377278030227591530497865873919513650246274195899088374387331594287372029770620207120213038572175933211162413330422773742416353553587977065309647685886077301432778290328894795818404378858567772932094476778669357537460048142376741194182671636870481056911156215614357516290527351224350080604653668917458196549482608612260750293062761478813268955280736149022525819682815051033318132129659664958159030421238775645990973296728066683849166257949747922905361845563741034791430771561168650484292490281102992529678735298767829269040788778480262479222750735948405817439086251877946890045942060168605142772244486272469911146200149880662723538837809380628544384763053235070132028029488392008132135446450056134987017834271106158177289819290656498688081045562233703067254251277277330283498433595772575956224703707793387146593033088629699440318332665797514676502717346298883777397848218700718026741265997158728035440478432478674907127921672898523588486943546692255101337606377915164597254257116968477339951158998349081888281263984400505546210066988792614558214565319696909827253934515760408613476258778165867294410775358824162315779082538054746933540582469717674324523451498483027170396543887737637358191736582454273347490424262946011299881916563713847111849156915054768140411749801454265712394204425441028075806001388198650613759288539038922644322947990286482840099598675963580999112695367601527173086852756572147583507122298296529564917835071750835741362282545055620270969417476799259229774888627411314587676147531456895328093117052696486410187407673296986649236437382565475022816471926815559883196629848307776666840622314315884384910519058281816740764463033300119710293036455866594651869074475250837841987622990415911793682799760654186088721626654886492344391030923256910633775969739051781122764668486791736049404393703339351900609387268397299246478483727274770977466693599784857120156789000241947269220974984127323147401549980920381459821416481176357147801554231599667838534854486406936410556913531335231184053581348940938191821898694825383960989942822027599339635206217705343572073396250574216769465101608495601439303244304271576099527308684609204422226103154229984444802110098161333824827375218998738205315164927134498105950159974800571591912202154487748750103473246190633941303030892399411985006225902184164409988173214324422108554248620896250260604398180189026317781146617454999771440665232863846363847001655618153861098188111181734191305505024860345856755585637511729774299329074944236579668332700918367338977347901759248885660379952771540569083017311723894140326159612292912225191095948743805673381278538616491842786938417556898047100859868372033615175158097022566275200160956192229925401759878522038545913771783976389811198485803291048751666921195104514896677761598249468727420663437593207852618922687285527671324883267794152912839165407968344190239094803676688707838011367042753971396201424784935196735301444404037823526674437556740883025225745273806209980451233188102729012042997989005423126217968135237758041162511459175993279134176507292826762236897291960528289675223521425234217247841869317397460411877634604625637135309801590617736758715336803958559054827361876112151384673432884325090045645358186681905108731791346215730339540580987172013844377099279532797675531099381365840403556795731894141976511436325526270639743146526348120032720096755667701926242585057770617893798231096986788448546659527327061670308918277206432551919393673591346037757083193180845929565158875244597601729455720505595085929175506510115665075521635142318153548176884196032085050871496270494017684183980582594038182593986461260275954247433376226256287153916069025098985070798660621732200163593938611475394561406635675718526617031471453516753007499213865207768523824884600623735896608054951652406480547295869918694358811197833680141488078321213457152360124065922208508912956907835370576734671667863780908811283450395784812212101117250718383359083886187574661201317298217131072944737656265172310694884425498369514147383892477742320940207831200807235326288053906266018186050424938788677872495503255424284226596271050692646071767467502337805671893450110737377034119346113374033865364675136733661394731550211457104671161445253324850197901083431641989998414045044901130163759520675715567509485243580269104077637210998671624254795385312852889930956570729218673523216666097874989635362610529821472569482799996220825775840988458484250391189447608729685184983976367918242266571167166580157914500811657192200233759765317495922397884982814705506190689275625210462185661305800255607974609726715033327032310025274640428755556546883765838802543227403507431684278620637697054791726484378174446361520570933228587284315690756255569305558818822603590006739339952504379887470935079276181116276309771257983975996526612120317495882059435754883862282508401408885720583992400971219212548074097752974278775912566026443482713647231849125180866278708626116699989634812405803684794587364820124653663228889011636572270887757736152003450102268890189101673572058661410011723664762657835396364297819011647056170279631922332294228739309233330748258937626198997596530084135383241125899639629445129082802023225498936627506499530838925632246794695960669046906686292645006219740121782899872979704859021775060092893328957272392019589994471945147360850770400725717439318148461909406269545285030526341000565022226152309364882887122046454267700577148994335147162504252365173710266068647253458120186683273953682547456536553597546685788700056988360286686450740256993087483441094086086303707908295240576731684941855810482475304758923392801571302824106234999945932390521409856559565661346003396150515164758852742214732517999548977992849522746029855666700811871200856155016457400484170210303038996339253337466556817824410737409336919294104632307731994759826307383499600770372410446285414648704116273895649834555162165685114551383822047005483996671706246467566101291382048909121117229386244253158913066987462045587244806052829378148302622164542280421757760762365459828223070815503469404938317755053305094698999476119419231280721807216964378433313606760676965187138394338772485493689061845700572043696666465080734495814495966306246698679832872586300064215220210171813917325275173672262621454945468506006334692713838311715849753092643252486960220059099802663765386225463265168414963306369548086551101256757717890616694758344043486218485369591602172030456183497524162039926441331651884768606830642004858557924473340290142588876403712518642229016333691585063273727199596362912783344786218887871009533753551054688980236378263714926913289564339440899470121452134572117715657591451734895195016800621353927175419843876163543479806920886666227099512371706241924914282576453125769939735341673046864585181979668232015693792684926999983992413571941496882273704022820805171808003400480615261792013978945186295290558440703738300533552421153903385185829366779190610116306233673144419202893857201855569596330833615450290424822309297087124788002017383072060482680156675397593789931793515799958929562156307338416294599900276730832827716595064217966523190439250543226753731811755315476780739470338931185107297724318378972674957455778183345495942317353558291046967315391275975687281861691161083156337232639968881490543943261197182274996791176628553401860198315809629981791107208804992292016062059067271273599461871634945774995805337947187105456452579396024210259136415528398395201773012712514892051061708228008339985665786646920737114269682301770416324829479409558694699089379165191006305185352102345189798127619143061864362703081977124992751056732909481202057747100687703379708934229207183903744167503493818836342229284946790660285674293251642569044363473087656797056595677285291081242733154406580199802711579126254172797452862574865921933293805915239524735518887119860391319654287576290190503964083560246277534314409155642181729459941596061979622633242715863425977947348682074802021538734729707999753332987785531053820162169791880380753006334350766147737135939362651905222242528141084747045295688647757913502160922040348449149950778743107189655725492651282693489515795075486172341394610365176616750329948642244039659511882264981315925080185126386635308622223491094629059317829408195640484702456538305432056506924422671863255307640761872086780391711356363501269525091291020496042823232628996502758951052844368177415730941874894428065427561430975828127698124936993313028946670560414084308942231140912722238148470364341019630413630736771060038159590829746410114421358321042574358350220737173219745089035573187350445827238770728271406162997919629357224104477155051652535867544109395079218369015261138440382680054150924346511711436477899444553993653667727589565713987505542990824585609510036934663100673714708029927656933435500927189854050109917474979991554392031908961967615444686048175400695689471463928245383807010444181045506171305160584355817521032338465829201071030061124283407458607006060194830551364867021020364708470807422704371893706965688795617928713045224516842027402021966415605280335061293558739079393524404092584248380607177444609964035221891022961909032569042381374492494906892314330884224399631396391545854065286326468807581148748371408284176455226386313520264894016262494802388568231599102952620337126449279901938211134518446387544516391239377974190576649911764237637722282802318465738050121277809680315691477264910257503508758792248110223544524410872448565700755187132146592093548504552829170749596775404450779494836371756062326925757412813110241910373338080434325310884694831555729402265394972913817581338619457057799561808755951413644907613109617155928376585840036489374076822257523935988731081689667688287403837192827690431514106997678303819085690713091931340846019511147482766350724676534922040058626677632935516631939622498979912708004465982264899125226813124300528104995058595676527123591494442612554437618645029202881358582871789577224116380815161831603129728796987480139828621645629196153096358337313619724773332353025466571196902611237380629030242904275794549030022660847446513161741691916851746464945459696005330885252792083472495235473110674109099223541055506299687642153951249355986311346661725116890785633328935569150449485189113488301876365100638502565916433021928565596263914382895068324838727165616560111531517055222955765944972454788815532316417453267167978861141165355597588331979638070962998880767303616940317736448140427867784251232449974693421348217179595190698204602997172001174857303889719205597414742453011135869766256607770970225633261701108463784795555258504578058879440756064974127974530918418405207558526462208821483646754652237609210787539190454684852349759986044943322828073120679922402477507514105890774627334319091255451352225329275913842047384603056163154236552935312278389759446515787337343463172280001031380425481404022090580405056003860937403435068863081434683848900708938565050027569059678069404698435184535134141031615133683043714786642925389717165978629010728400758939700388317742648163725113277369926827709465342583596111881955092462062153978121197244762623771534452048069819082524943963962251113831177428978535825590832490480497516047104257569753442551515779815600370847230603484753977513688390404316017486248871339311818523029425425676202485688393970836748788453789172574145155917919035398535077200900594979352939459631213445503368260690059828717723533375221941915547303742062343262892968397015058892191112049249864792053410872349115430987182160055762209075732304626106597744947658346313025598636315029959672352476943975462530206788193304372284800209305354155640664838569378144603138697563459200233462606995955513484754147891180830329816421587452922952678937925647752029052675349356673744293182673374571642465407748267901046778759085408130531447176455869894169668940436489952465247443988349583871206296485413357553813419500498743813369062703973874586604296871595820715766599826607317005624465541763024501349159567288942619746144496908671655859782729228702723774835097362901019130417812735773037781804081589136005207315806941034305003184349342360269244733060013861119781774472669608928321052543116496033420102032603863672532889648333405862204843616575362001468405476649666473566979572953394809138263703324220930839366954980688240491622063147911494642042500022450413425558561937442905257252436320054487441524307305215070491020434076572476865095751174125413729531644521765577235348601821566833352520532830000108344008762266843817023235605645158256954177359197813649975559601912567744942717986360045847405209290089397315276024304951653864431388147876977541478757432610159879709758855625806766197973098472460769484821127948427976536607055051639104415022554420329721292033009353356687294595912327965886376486894188433640548494009574965791657687213927330153555097865114767947399690623184878377515462613823651665956337209345708208301840482797005728071432925727577436229587047361641609731817241594204270366066404089740245521530725227388637241859646455223673260411164598464020010216920823315155388821071527191267876531795071908204525100447821291318544054814494151867114207103693891129125012750853466337717749376016543454696390042711129829255096830420665725364279472200020835313883708781649957189717629338794854271276882652003766325924561614868744897471519366219275665852462114457407010675380427564184440834805203838265052601698584060084788422421887856927897751810442805474427229455167420335686460609977973124950433321425205053675790499520783597650415379001132579536040655172654879022173595444151139429231648950663177813039057462082449171921311864129633704661406456900178942356738775523130952785912774533241855442484484493664210731348819180640189222317302156645813473186449997905781662091469870718039388885781280740226363602294114354869871402143572055947730892808653678920201935102605361567924483276749476117858316071865710310842200560259545115191391309119544447844361032741876102338843391687589233423790859841968266525610628751237572318491474951945985728897934981791761822652480408237128109790772638864286067917082288575852703470839714561619926247844794692794996845945632382702297364173503430783194115698247820013290851202878474805860188960045901745974055630732714487679085288867978809970695240681006625611440014983413580889737246844064948857074167687916413224205373654067330186392497910915474785959163865597507090581175924899502214799250945635582514315814464060134283490422798357939659258985200763845646681640732681928346007767285876284900068874564639274964415904034033672337814491597032941787294155061054129515400159393851663929325677429557549480046658273579653990940233543644649376827272541873627547532976808190325336141086433084237771738995221536763095302045902438694632702895293994483013577589081214884558493819874505920914067209522469096263076941753340983698859363700314973728977996360018626500174929290087931189997822963712306642297996163582572600112288983647651418045975770042120833949364659647336464289044499325396227091907373705772051322815957863227591912786054297862953188615559804728160710864132803585400160055575686855791785977899197902656592621283007225351401525973569300729015392211116868504740402172174442051738000251361000494534119324331668344243125963098812396962202358858395587831685194833126653577353244379935683215269177042249034574534858913812582681366908929476809052635560638119661306063936938411817713545929884317232912236262458868394202889981693561169865429884776513118227662526739978808816010470651542335015671353744817086234314662531190291040152262927104099285072418843329007277794754111637552176563589316326636049381218401837512818884771168975479483767664084842753623074019542183217985496260666590347925816342392670947839907062923166535037285019751324813803837070894638925470887039085723581006130628646664710006104352115778926613432214655311411882596942926284522109026688414975763341554921135581254616558078273470115814006008345762133130389987843270653719956709570847385786092649188858378739239165554263577301292243641604062551736892335636568854365851646207821875741724364525814143487632761341752707376754922276287782264765154315341585713773522730335403376364204258034257264749686217823666951353410677378421131371131987373222891805275062812277716412494412401207125954319991746574745892582613712825555535080404143944557295994554635608487251339462936358940832098964801619583130429720964794128539388996265368928263807677168759588502216464582430940165009688797366157733560316836710386895228270941509545222744002735499253670214715994056544813842186380128799900820933576320736369405991424263718294000613741900579513096298545330748197802568301089672873802234820488862973130369689882640657904781562389778485365025691064231795736025330908763271784911189748432246868086340383964176127605788646574472284824932687443062551220506955168464669477183681911432873544815836350548146411099960143390595799766290646881295025039150923633011076070632863317393378149693380247580035052789782755750928604039420506342939327064636161031822879248152679306862749237275631852225654266008556849497720285909150930495425967473648331437236349555448901598668408362176913559656039519670425368863482369587129462524759031776813184977588276576740482558136502103649585505703259219957675334264223783723586058509403583977103476670644788640831109650302565215607464019652716999732373465237173456595514559493098166644006211599349133180135150528651842178828026343325934755850761168697709125580056185683710540856081249519403148064618719402577663285267019698387567561524696759028106864896869293315954352097687527137201616160931174250199709289684940034696242325688410665113304377412256176258658941236728171145526423894512631717834790276921171452887352955019336759218908006048633737786728180610254782570436788449503518925787499836694785908612975543084122677060954347612133717433156783790162012337237023338316414706428592185977610158232721997915062871868186750981665537745013020880333904353639770263363809098526494532628146558065546504823486429495390613257400496912888340518222933644476683855037967975809619983575807027759535968788226194659612223044549275600274955168583542582295336042834426318478068825395450746691877897765406038432512843812811316856204608617289408229658626174420766920297427930088129519854678713548623236610413216581279267151545961594352593456757445992307889205519540082316409719591250025455237503106735639748835542480449681383030671851931491335789202123605308199952020584503423499932150962634977812456658304680581824563524814625849331926195406884818446445248429486063016169476663242625231476322371109695369483824482316410396224507675405614287468267835723704895606990652792688455844512046654853378534026646645042339638488257719874953611300494215593735545211926186721478265416885604094928290056616883807637656690510740892510549165222968878676968631652514917701499900066637344546120262780701925698706225540928945194718778004306130021828287425867048748480826948573444778244078734102710824870269523830804910960482013901294024631244800159336670212658317677879752965963472576894326540435889267293950687860830626266263287392087327302547910099932113388977807814336728791448768373686467748528777737403547472871644217767820712964506270880978637928144071192505141148004907055608097229299792441471062852247029870699869227676341773513258602908903875707454368077876422385333700692089616351009233587303986543906071880952557553380364725895007306772122528078179471056481171378557451057691044322925429024149433588396093679321361696954251299731031032804436954501929843820842383121265825740594509426942777307124802176915781835720087170538773256017987133005505911377823841791640280841409623820847637393013930778428554545222367559824666250608754284876104145661362227642405914304455580856318180935230407793891614902116292400515074914068443203230365609954878620999194306564455332547135557365318516011700321550690787716752062881527885897149410320986984083048966524351030502444679931779147659103428949129054120361601695671222140806369405940304552186212879933092856231022418446365289097444640151986623183881962444822590783585914043686193019041458962693878907034982169868696934448086213990534591792826654304798207219634134755646525483143771156678459077797196510772468000293581546267646310224279007313631352522067062951125935874473134186492497282784796644585448962932905262058065248588707020879389134476083344653170939242408249328008915731319541348311820927752486880548733943315867562666122179355051190609992911379445634995627391898459029021713155706096267881673302940198464237390445098028030948975981259252055850973537436556825780313681902007151675693827281818824587541710721180806556448039122504537089422695358382192535075692834095639859265599740391316709290043996275976830375217503360879028295673068862263077729733533853682668734519035709709687322323738300494090123239274318759046526327095178406267264828893646896593219169521106361729757074376148061601331104911692271318609404145014842866423634716982892418180484365230538864559809839273836490685480823014267803143937440431807822678779494006206489151248952516543005634448375046751754207043313372486870633237561645232360481932024377596890914783372179553676992603235715185513391098402739063753280702313301755754269396202629423910945323537910125948964941812563672992967084250667599803456273455598559628512281414582556024841783305645240508450065988755987518601335860624932784487772006842296591945516539562982960591610046578907214842054861830418175604559815168088031783080261445994444677918012432146400983610678683412974872596729258786806223080115822026289014364459002301645823666709265571264559925790622304745235625575111770791512002789380975775468546121017307522799241407026308137792971909461413145802081087738121624539858769697371425881836152605069380926917712087321915005831977113322793572385071940612761291872572099404930250277748156614021327434743881966413330052634229082906400927944924808556131183440161804801357032507836323938921567643159620442612809700944107776130638909071294456394056601559246025454204771186140420155233371270501377121034570009578009389265329385720478576508777149663403003562380595757191609382171312222810465858388943507176431939973012661591423837170284400120399485880996231859472474858776584355077006934099220340378772192728370301380838144394114984971730766162961342059105014814283949700695951676939041557902856356911055547312684571497449635320554677940775184056667637222969090346128706829887104278761090090999160443821794511763620835379716161833124364431267855435550800507986124664397724135502128238026726719914989727248512981287283697489276420792868666970177259794407858155909332508554131299946581118527691652464790819119384233275897699573012098103009171001695718791616942270079528915191912521053891838538959315167400505723817401030621004380243011187977704252328073236575129609372456053680037516596164236147709330391224409752871732067976128120428026739256557305675931512645750047875756531854825821411574030473147492511910835615765732002546109686701890307648531373832912682481741181359032826625082549313211431478953352317043989053928534946642886074268371824902498092479487226633686823799580875637040808655649321905489637785549531167397935270799470452399153297534358690514105864096534514182896474439367182852711843560799285895978176543950113088848419163516673213692860830956744502801800373716458009168082972708715609185038654053436660045504985624687376022557041595800250174095361839287643458003670864954057941720085136357127163768323493134230703821274484501440529541695374381945459456533165140990993722722801019654652726227831512103467686166826131471843610025517863247950150022953695466317739589344131481485834694374523981159954666071205997794363440185078360899108948073419633939259318973940943110042116729120199722626609871927014024105805515315100109804996044147291039451030312664114726736839973315035036742741546992633165270432940675237449075056739508929674779115800864399992564817208847429250821546279856079127768611946086210349405535850134472190244543824521089284409498132717010673966471114931896789977661595488186193176900175027901783824624387873831483279500879026433992577026588005849778984624295660321276945810824348129690840972550671054732471317254997191901039553305847040728081693158626093886019147689944137673621432083607375131574376316754666479186753896571555100850626810005119827486807780592667765654100834778571024250133253391587384761024129794736751001163498977803745930025457609870671092153597115178252014281216647543034075128600240297038428615984289816602143429849088917359682192284469123035904329877231843309914187264674607558318725713138832356015809009594182530207799397648462597901883341793830920965841463574411985878296475850943053008148341821747826603773762252997703468752903517310792083220038080809212164346586817989810504274375385786789186350517717501606531826406928883250135919517178537687865881752366421534010961295763074762648070312757365787762352859057153932484576503944390496668087711899192498933896524852395536795827530614167131757915756386606004839994179548705868209201195154952031294562451315422506574858629161606523796643010172693950282294667489681746821163996794950294284013099235901278250437428192557634533217576162292751110598368271567229778620053722932314082887058749444060116236521627717558503013451471452765841864277071769968435499620257547431811994883385806759692359580622165832464092095350648357935817742903018315351290014321495518177456908388719320697769695657771754499149911431368950836160692539606469893374870942933219185601299108564470256257163505508620689240297589684714283678684735455533583477652536156578189996983068654671736445996343136468195427420490472433064675001442697508322369013083895492637066778406531328664886080129513771720847581157719491012345141774941482773580041432667332379617716965698582785832300505265883502247868050648201444570593197343382923860072601696510903258980909912837652275381493529845099414966933862815568031306981064525192703818515872648691762563239441425216118427769145067718411735714396681005615483952443154944864238384298900399826113322468963346522104692545137969276009719645338955332105584245640187448611050959111766828942711640054010503770420346052521318228045892998637903572350665108782350043349942391285236308896510989246641056331584171142885304143772286629832318970869030400301325951476774237516158840915838059151673504519131178193943428482922272304061422582078027829148070426761629302539228321084917759984200595105312164731818409493139800444072847325902609169730998153853939031280878823902948001579008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Try that in your favorite programming language and let me know how it goes :).&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>programming</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>How to Build Your Electron Project Using CircleCI</title>
      <dc:creator>Mike CK</dc:creator>
      <pubDate>Mon, 04 Nov 2019 17:37:05 +0000</pubDate>
      <link>https://dev.to/mikeck/how-to-build-your-electron-project-using-circleci-1982</link>
      <guid>https://dev.to/mikeck/how-to-build-your-electron-project-using-circleci-1982</guid>
      <description>&lt;p&gt;All this time I have been wondering what my first article on dev.to would be. I have been writing ocassionally on &lt;a href="https://www.elevatika.com/"&gt;www.elevatika.com&lt;/a&gt;. As it seems, nature has its ways. &lt;/p&gt;

&lt;p&gt;I was trying to figure out how to build an &lt;a href="https://electronjs.org/"&gt;electron&lt;/a&gt; project for the Windows platform on MacOS (some people still prefer OSX). Why was this a problem? &lt;/p&gt;

&lt;p&gt;It appears one can't simply build an &lt;a href="https://electronjs.org/"&gt;electron&lt;/a&gt; project for Windows using a Mac without sacrificing their unborn children, sometimes. &lt;/p&gt;

&lt;p&gt;So, dev.to and the Internet, here is my story.&lt;/p&gt;

&lt;p&gt;In 2018, I built a security system using JavaScript in the front-end, the backend and in the accompanying desktop and mobile apps. The part that is relevant to this article is &lt;a href="https://electronjs.org/"&gt;electron&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I made the desktop app using &lt;a href="https://electronjs.org/"&gt;electron&lt;/a&gt; + &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt; based on a boilerplate I found through &lt;a href="https://vuetifyjs.com"&gt;Vuetifyjs.com&lt;/a&gt;. I had no issue building for Windows back then since, well, I was on a Windows machine.&lt;/p&gt;

&lt;p&gt;When you run the electron build command without any flags, it will detect the operating system you are running on and build for it as the default target. Therefore, back in 2018, I was on a Windows machine and my target build was for Windows. No problem.&lt;/p&gt;

&lt;p&gt;However, last week, a friend of mine wanted me to modify the code a bit. I thought it would be simple. I cloned my &lt;a href="https://bitbucket.org/"&gt;Bitbucket&lt;/a&gt; repository on Mac, made the changes and everything looked ok. &lt;/p&gt;

&lt;p&gt;Time to build the Windows binary came and I simply edited the build script. I added &lt;code&gt;-w&lt;/code&gt; which tells the &lt;code&gt;electron-builder&lt;/code&gt; to target Windows. By the way, you can do &lt;code&gt;-mwl&lt;/code&gt; to build for Mac, Windows and Linux at the same time. My build script now looked like so:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node .electron-vue/build.js &amp;amp;&amp;amp; electron-builder -w&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 then ran &lt;code&gt;yarn build&lt;/code&gt; and waited.&lt;/p&gt;

&lt;p&gt;Build failed. I encountered the following error message:&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="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="nx"&gt;wine&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Exit&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ENOENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="nx"&gt;wine&lt;/span&gt; &lt;span class="nx"&gt;ENOENT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Granted, things were not going according to my expectation and I thought I would find a simple fix. In fact, I confess I love build errors as long as they are helpful. In this case, I saw &lt;code&gt;wine&lt;/code&gt; and recalled that's the thing that allows you to run Windows apps on &lt;code&gt;*nix&lt;/code&gt; systems like Mac or Linux. I popped up a new terminal window and typed: &lt;code&gt;brew install wine&lt;/code&gt; and waited for one hour as &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt; poured it's magic onto the system. Why does &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt; take forever to install stuff? &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Trust me: everyone who hates &lt;code&gt;npm install&lt;/code&gt; has never sat and watched &lt;code&gt;brew install&lt;/code&gt; for 5 years and a some hours, give or take.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When wine was done installing, I did &lt;code&gt;yarn build&lt;/code&gt; again and I got the same error.&lt;/p&gt;




&lt;p&gt;I love Google. &lt;/p&gt;

&lt;p&gt;That was my next stop. I tried searching for a solution and then I remembered how hard it is developing electron apps. It is not as easy as people make you think. As a front-end developer, you will not be at home and you will always have to accept it when you cannot find a solution of issues you face on Google.&lt;/p&gt;

&lt;p&gt;I have a basis for this argument. &lt;/p&gt;

&lt;p&gt;For example, there is not enough helpful stuff out there for electron yet. Don't people build for Desktop? I know there are some forums and platforms like StackOverflow where you can just ask a question and get an answer. I mean, no platform is as friendly as StackOverflow, right? &lt;/p&gt;

&lt;p&gt;Who am I kidding, I don't even recall the name of my profile on StackOverflow. &lt;/p&gt;

&lt;p&gt;You see, my grandmother and her great great grandmother got insulted when I posted a question 4 years ago and got down voted by some random smarts. I never ask questions over there anymore. &lt;/p&gt;

&lt;p&gt;Besides, all the JavaScript's &lt;code&gt;undefined is not an Object&lt;/code&gt; questions have been answered already. Therefore, being a web developer, I can just read answers over there. Also, I am the kind of person who prefers real-time answers and therefore when my immediate friends don't have answers and Google is also failing me, I decide to figure the solutions on my own.&lt;/p&gt;




&lt;p&gt;Building for Windows on Mac was not going to work. But &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt; is amazing for that. In fact, I use &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt; to build static websites and deploy to ftp servers all the time.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;.circleci&lt;/code&gt; folder on my project root and added the following &lt;code&gt;config.yml&lt;/code&gt; file inside it. This script is a modification of one that can be found on &lt;a href="https://github.com/joeireland/electron-circleci/blob/master/.circleci/config.yml"&gt;Github&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_windows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;electronuserland/builder:wine&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn &amp;amp;&amp;amp; yarn run build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;store_artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/root/project/build&lt;/span&gt;

&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build_windows&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While doing my research, I found &lt;a href="https://github.com/joeireland/electron-circleci/blob/master/.circleci/config.yml"&gt;this config&lt;/a&gt; on Github. In my case though, I wanted to download the resulting &lt;code&gt;.exe&lt;/code&gt; file after building the project and I found out I can simply add the following section on the list of steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;store_artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/root/project/build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this allows you to download the resulting files. You just click on the file name and it downloads immediately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m6OlbP-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/hd9f42l28ric6gqn925v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m6OlbP-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/hd9f42l28ric6gqn925v.png" alt="Build Artifacts on CircleCI for an electron project built for windows target" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;My simple setup doesn't just hold true for building electron apps. In fact, with how cool and simple CircleCI makes automated builds whenever you push to your git repository, you can build any project and download the files. You get 250 build minutes free every week. My electron project took just 3 minutes to build.&lt;/p&gt;

&lt;p&gt;Thank you for reading. Ask any question in the comments below. Cheers!&lt;/p&gt;

</description>
      <category>electron</category>
      <category>docker</category>
      <category>devops</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
