<?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: CK</title>
    <description>The latest articles on DEV Community by CK (@christopherkapic).</description>
    <link>https://dev.to/christopherkapic</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%2F663116%2Fafb31ce2-8b6c-4c91-9572-ea538d83853c.png</url>
      <title>DEV Community: CK</title>
      <link>https://dev.to/christopherkapic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christopherkapic"/>
    <language>en</language>
    <item>
      <title>Having trouble with PWA &lt;Audio /&gt; and S3?</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Sat, 03 Jun 2023 13:37:57 +0000</pubDate>
      <link>https://dev.to/christopherkapic/having-trouble-with-pwa-and-s3-3fpo</link>
      <guid>https://dev.to/christopherkapic/having-trouble-with-pwa-and-s3-3fpo</guid>
      <description>&lt;p&gt;Appended info at the end of the article.&lt;/p&gt;

&lt;p&gt;Hi all,&lt;/p&gt;

&lt;p&gt;I'm writing a PWA which requires audio playback from S3 with presigned URLs. The presigned URLs are important because the bucket &lt;em&gt;cannot&lt;/em&gt; be public.&lt;/p&gt;

&lt;p&gt;I wrote a little audio player, and on the web on desktop, it works every time. However, on PWA on iOS (and maybe Android, I have yet to test this), the audio playback is slow and works about half of the time. I'm curious if any of you have experimented with this, and whether you have a solution for it.&lt;/p&gt;

&lt;p&gt;Here is the full component fwiw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useRef, useEffect } from "react";
import {
  ArrowUturnLeftIcon,
  ArrowUturnRightIcon,
  PlayIcon,
  PauseIcon,
} from "@heroicons/react/24/outline";

import { api } from "../../utils/api";
import Spinner from "../ui/Spinner";
const skipTime = 15;

const buttonStyles = `
  w-6 items-center
`;





const AudioPlayer = ({
  src,
  recordingId,
  duration
}: {
  src: string;
  recordingId: string;
  duration: number;
}) =&amp;gt; {
  const audioRef = useRef&amp;lt;HTMLAudioElement&amp;gt;(null);
  const seekRef = useRef&amp;lt;HTMLInputElement&amp;gt;(null);
  const [_duration, setDuration] = useState(duration);
  const [currentTime, setCurrentTime] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [lastTime, setLastTime] = useState(0)
  const [loaded, setLoaded] = useState(false);
  const updateLT = api.recording.updateListenTime.useMutation();
  const [audioUrl, setAudioUrl] = useState&amp;lt;string | null&amp;gt;(null);

  const downloadAudio = async () =&amp;gt; {
    const response = await fetch(src);
    const blob = await response.blob();
    const objectUrl = URL.createObjectURL(blob);
    setAudioUrl(objectUrl);
    if (seekRef.current) {
      seekRef.current.value = "0"
    }
  }

  useEffect(() =&amp;gt; {
    downloadAudio().then(() =&amp;gt; {
      setLoaded(true)
      return;
    }).catch(() =&amp;gt; {
      console.error("Could not load audio")
    })

    return () =&amp;gt; {
      if (audioUrl) {
        URL.revokeObjectURL(audioUrl);
      }
    };
  }, [])

  return (
    &amp;lt;&amp;gt;
      {
        audioUrl &amp;amp;&amp;amp;
        &amp;lt;&amp;gt;
          &amp;lt;audio
            controls
            ref={audioRef}
            onDurationChange={(e) =&amp;gt; {
              const target = e.target as HTMLAudioElement;
              const dur = target.duration;
              if (dur !== Infinity) {
                setDuration(dur)
              }
            }}
            onTimeUpdate={(e) =&amp;gt; {
              const target = e.target as HTMLAudioElement;
              if (seekRef &amp;amp;&amp;amp; seekRef.current) {
                seekRef.current.value = (target.currentTime * 1000).toString();
              }
              setCurrentTime(target.currentTime);
              target.currentTime.toString();
              if (target.currentTime - lastTime &amp;gt; 2) {
                setLastTime(target.currentTime);
                updateLT.mutate({
                  recordingId,
                  progress: target.currentTime,
                  duration: _duration,
                });
              }
            }}
          &amp;gt;
            &amp;lt;source src={src} type="audio/mp3" &amp;gt;&amp;lt;/source&amp;gt;
            Your browser does not support the audio!
          &amp;lt;/audio&amp;gt;
          &amp;lt;div
            className={`
          align-center
          rounded-md
          flex
          w-full flex-col
          justify-center
          bg-gray-200 text-gray-900
      `}
          &amp;gt;
            &amp;lt;div className={`flex h-8 w-full items-center justify-center pt-2`}&amp;gt;
              &amp;lt;span className="mx-2 w-8"&amp;gt;
                {Math.floor(currentTime / 60).toFixed()}:
                {String((currentTime % 60).toFixed()).padStart(2, "0")}
              &amp;lt;/span&amp;gt;
              &amp;lt;input
                type="range"
                min={0}
                max={_duration * 1000}
                ref={seekRef}
                className="h-0.5 cursor-pointer appearance-none rounded bg-white"
                onChange={(e) =&amp;gt; {
                  const target = e.target as HTMLInputElement;
                  if (audioRef &amp;amp;&amp;amp; audioRef.current) {
                    audioRef.current.currentTime = Number(target.value) / 1000;
                  }
                }}
              /&amp;gt;
              &amp;lt;span className="mx-2 w-8"&amp;gt;
                {_duration ? Math.floor(_duration / 60).toFixed() : 0}:
                {_duration
                  ? String((_duration % 60).toFixed()).padStart(2, "0")
                  : "00"}
              &amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div
              className={`
        flex w-full justify-center gap-4 pb-4
        `}
            &amp;gt;
              &amp;lt;button
                // disabled={audioContext!.tracks!.length === 0}
                className={`${buttonStyles}`}
                onClick={() =&amp;gt; {
                  if (audioRef &amp;amp;&amp;amp; audioRef.current) {
                    audioRef.current.currentTime = Math.max(
                      audioRef.current.currentTime - skipTime,
                      0
                    );
                  }
                }}
              &amp;gt;
                &amp;lt;ArrowUturnLeftIcon /&amp;gt;
              &amp;lt;/button&amp;gt;
              &amp;lt;button
                // disabled={audioContext!.tracks!.length === 0}
                className={`${buttonStyles}`}
                onClick={() =&amp;gt; {
                  if (audioRef &amp;amp;&amp;amp; audioRef.current) {
                    audioRef.current.volume = 1
                    if (playing) {
                      audioRef.current.pause();
                    } else {
                      audioRef.current.play().catch((e) =&amp;gt; console.error(e));
                    }
                  }
                  setPlaying(!playing);
                }}
              &amp;gt;
                {loaded ? &amp;lt;&amp;gt;{playing ? &amp;lt;PauseIcon /&amp;gt; : &amp;lt;PlayIcon /&amp;gt;}&amp;lt;/&amp;gt; : &amp;lt;Spinner /&amp;gt;}

              &amp;lt;/button&amp;gt;
              &amp;lt;button
                // disabled={audioContext!.tracks!.length === 0}
                className={`${buttonStyles}`}
                onClick={() =&amp;gt; {
                  if (audioRef &amp;amp;&amp;amp; audioRef.current) {
                    audioRef.current.currentTime = Math.min(
                      audioRef.current.currentTime + skipTime,
                      _duration
                    );
                  }
                }}
              &amp;gt;
                &amp;lt;ArrowUturnRightIcon /&amp;gt;
              &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/&amp;gt;

      }
    &amp;lt;/&amp;gt;

  );
};

export default AudioPlayer;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I thought if I downloaded the full file as a blob then it might work, but I'm still getting slow/no playback on iOS. This is the same behavior I got when I tried to stream directly from the S3 presigned URL.&lt;/p&gt;

&lt;p&gt;Let me know if you have any suggestions.&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

&lt;p&gt;Update:&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://blog.prototyp.digital/what-we-learned-about-pwas-and-audio-playback/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; from Prototyp, PWA audio support from Apple is &lt;em&gt;intentionally&lt;/em&gt; unreliable because of Apple's desire to maintain a monopoly on the App store (eg: forcing Spotify to use an official app instead of a PWA).&lt;/p&gt;

&lt;p&gt;I am going to look into alternatives for audio playback, potentially a native app.&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>s3</category>
      <category>nextjs</category>
      <category>ios</category>
    </item>
    <item>
      <title>Choosing an Open Source Headless CMS</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Fri, 09 Sep 2022 13:39:53 +0000</pubDate>
      <link>https://dev.to/christopherkapic/choosing-an-open-source-headless-cms-4ic2</link>
      <guid>https://dev.to/christopherkapic/choosing-an-open-source-headless-cms-4ic2</guid>
      <description>&lt;h2&gt;
  
  
  What is a CMS?
&lt;/h2&gt;

&lt;p&gt;T﻿raditionally, a CMS and a website are hosted on the same server. Wordpress, for example, is a CMS and website combination in which the admin can log into the CMS to get editing privileges directly on the site. More recently, there has been the &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAMstack&lt;/a&gt; trend, part of which is the introduction and proliferation of the &lt;a href="https://jamstack.org/headless-cms/" rel="noopener noreferrer"&gt;headless CMS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean to be headless?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The webserver and the CMS are hosted separately.
&lt;/h3&gt;

&lt;p&gt;I﻿nstead of hosting the website and the CMS on the same server, the site and CMS are separated. This has several pros and cons.&lt;/p&gt;

&lt;p&gt;W﻿e'll start with the cons first.&lt;/p&gt;

&lt;h2&gt;
  
  
  W﻿hat are the cons?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;U﻿sing a headless CMS requires a higher level of technical competence to get going. There are many competitive services which offer Wordpress hosting that require little technical savviness, but in choosing a headless CMS one necessarily must know how to host two servers, or outsource the CMS to a SaaS.&lt;/li&gt;
&lt;li&gt;O﻿ften, more custom development is required; you must know how to connect your site to your CMS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  W﻿hat are the pros?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;﻿If your CMS goes down, your site can still keep working. With options like &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;11ty&lt;/a&gt;, &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;, &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt;, and more for static site generation, your CMS only needs to run at build time. This gives you a lot more reliability, especially as modern serverless hosting options become more robust.&lt;/li&gt;
&lt;li&gt;T﻿here are far more options for site customization - you have the freedom to connect any framework to any CMS. For sites that require custom development, this freedom can be essential.&lt;/li&gt;
&lt;li&gt;H﻿osting options are more compelling when you enter the JAMstack world. Instead of a traditional webserver setup, using tools like Netlify, Vercel, CockroachDB, Planetscale, any of the myriad of frontend frameworks, and more can allow you to create an incredible and unique experience to your customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do I know which CMS to choose?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SpUzTeBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://christopherkapic.com/cms/choose-a-cms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SpUzTeBE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://christopherkapic.com/cms/choose-a-cms.png" alt="Choose a CMS diagram (explained in article)" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;U﻿ltimately, there are more options to consider than those which I can cover here. For a more comprehensive breakdown, I recommend that you check out &lt;a href="https://jamstack.org/headless-cms/" rel="noopener noreferrer"&gt;JAMstack.org's comparison&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;T﻿hat being said, if open-source is important to you, be it for privacy or avoiding vendor lock-in, my three favorites are &lt;a href="https://directus.io/" rel="noopener noreferrer"&gt;Directus&lt;/a&gt; (I am a contributor), &lt;a href="https://keystonejs.com/" rel="noopener noreferrer"&gt;KeystoneJS&lt;/a&gt;, and &lt;a href="https://www.netlifycms.org/" rel="noopener noreferrer"&gt;NetlifyCMS&lt;/a&gt; (not &lt;em&gt;technically&lt;/em&gt; a headless CMS, but still worth considering).&lt;/p&gt;

&lt;h2&gt;
  
  
  D﻿irectus
&lt;/h2&gt;

&lt;p&gt;D﻿irectus is... really cool. It can connect with your currently-existing database, provides a GraphQL API, is extensible, can be hosted in a serverless environment (Google Cloud Run), and is a pleasure to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  K﻿eystoneJS
&lt;/h2&gt;

&lt;p&gt;K﻿eystone is in many ways similar to Directus, except it uses a more developer-first approach to the database schema. You write a file that compiles into a Prisma configuration. This is nice in theory, but it makes using the same database as your currently-existing database more difficult. However, that may not be a problem since the idea of a headless CMS is to divorce the CMS from the website infrastructure anyway. Like Directus, it too can be hosted in a serverless environment (even Netlify).&lt;/p&gt;

&lt;h2&gt;
  
  
  N﻿etlifyCMS
&lt;/h2&gt;

&lt;p&gt;O﻿f this group, NetlifyCMS is certainly the oddball. Instead of connecting to a database and potentially a storage bucket, NetlifyCMS connects with your git repository. Using a service like Netlify, a new build of your site is created every time a piece of content is updated. For something like a blog, where content is read far more than it is updated, and the content is updated only by admin users, NetlifyCMS is a good choice. The best part about NetlifyCMS is that it can easily be used for site templates. Most starter sites I create use NetlifyCMS because users can simply deploy to Netlify and call it good.&lt;/p&gt;

&lt;p&gt;N﻿etlifyCMS is &lt;em&gt;not&lt;/em&gt; good for websites with content that must be updated by regular users (comments should not be handled by NetlifyCMS, for example).&lt;/p&gt;

&lt;h2&gt;
  
  
  H﻿ow to choose?
&lt;/h2&gt;

&lt;p&gt;E﻿ssentially, I argue the best simplification for how to choose is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;D﻿o non-admin users update any "server-side" state in your site? If not, go with NetlifyCMS.&lt;/li&gt;
&lt;li&gt;D﻿o you want to connect to your existing database? If so, go with Directus.&lt;/li&gt;
&lt;li&gt;I﻿f neither of the above apply, choose between Directus and KeystoneJS. You'll probably want Directus, but there may be edge cases in which Keystone is better (or, you might just have a preference for whatever reason).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;U﻿ltimately, there are many options for your choice of CMS, and the right one might not be here. This is just a framework I have decided upon after doing lots of research for my own projects and learning which CMS options work best for most of my needs.&lt;/p&gt;

</description>
      <category>cms</category>
      <category>netlifycms</category>
      <category>directus</category>
      <category>keystonejs</category>
    </item>
    <item>
      <title>I created a 🔥blazingly🔥 fast blog template with Astro and NetlifyCMS</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Thu, 18 Aug 2022 20:39:14 +0000</pubDate>
      <link>https://dev.to/christopherkapic/i-created-a-blazingly-fast-blog-template-with-astro-and-netlifycms-2kh</link>
      <guid>https://dev.to/christopherkapic/i-created-a-blazingly-fast-blog-template-with-astro-and-netlifycms-2kh</guid>
      <description>&lt;p&gt;This article was written and published on my &lt;a href="https://christopherkapic.com" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. I &lt;em&gt;did not&lt;/em&gt; interact with Medium or dev.to except to check that it was republished correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;a href="https://astro-devblog.netlify.app/" rel="noopener noreferrer"&gt;Astro Devblog&lt;/a&gt;?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A serverless (JAMstack) blog template intended to be deployed to Netlify which republishes articles to &lt;a href="https://medium.com/@christopherkapic" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; and &lt;a href="https://dev.to/christopherkapic"&gt;dev.to&lt;/a&gt;.
&lt;/h3&gt;

&lt;p&gt;Read about how to deploy the template for yourself &lt;a href="https://astro-devblog.netlify.app/article/how-to-use-astro-devblog" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or see &lt;a href="https://youtu.be/8c7_vX3XPDc" rel="noopener noreferrer"&gt;this YouTube video&lt;/a&gt;. Or, better yet, dive into the &lt;a href="https://github.com/christopher-kapic/astro-devblog" rel="noopener noreferrer"&gt;git repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Astro?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; is the hot new kid on the block, with compelling features like zero JavaScript by default and the Islands architecture.
&lt;/h3&gt;

&lt;p&gt;Previously, I was using &lt;a href="https://www.11ty.dev" rel="noopener noreferrer"&gt;11ty&lt;/a&gt; for my blog. However, Astro offers several advantages, including...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Islands architecture allows me to incorporate UI frameworks/libraries when necessary or helpful&lt;/li&gt;
&lt;li&gt;SSR futureproofs my stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have not yet rebuilt all of the features associated with my old blog, but I am optimistic that Astro is a good choice for my blog's future. In the near future, I will share the aspects of Astro that I enjoyed, as well as the pieces with which I found frustration. If you're interested in that, consider following me on &lt;a href="https://dev.to/christopherkapic"&gt;Dev&lt;/a&gt;, &lt;a href="https://medium.com/@christopherkapic" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, &lt;a href="https://www.youtube.com/channel/UCuXgDzDJhNAwvzvc62GnYwA" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, and/or &lt;a href="https://twitter.com/kapicode" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>medium</category>
      <category>dev</category>
    </item>
    <item>
      <title>Arch Linux Installation Tutorial (Tested on T530)</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Fri, 27 Aug 2021 08:05:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/arch-linux-installation-tutorial-tested-on-t530-kh7</link>
      <guid>https://dev.to/christopherkapic/arch-linux-installation-tutorial-tested-on-t530-kh7</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1l9vbuw421ojq51igw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1l9vbuw421ojq51igw1.png" alt="Arch Linux Thumbnail" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow along to install Arch Linux (tested on ThinkPad T530)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://archlinux.org/download/" rel="noopener noreferrer"&gt;Download the &lt;code&gt;.iso&lt;/code&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;h4&gt;
  
  
  Flash a USB
&lt;/h4&gt;

&lt;p&gt;Use &lt;a href="https://rufus.ie/" rel="noopener noreferrer"&gt;Rufus&lt;/a&gt; (Windows) or &lt;a href="https://www.balena.io/etcher/" rel="noopener noreferrer"&gt;Etcher&lt;/a&gt; (MacOS or Linux) to flash the &lt;code&gt;.iso&lt;/code&gt; to a flash drive.&lt;/p&gt;

&lt;h4&gt;
  
  
  Boot into the installation media
&lt;/h4&gt;

&lt;p&gt;Common boot buttons include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;F12&lt;/code&gt; - ThinkPads&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Connect to the internet
&lt;/h4&gt;

&lt;p&gt;Ethernet/virtual machine does not require any extra setup. For wifi, you will use the &lt;code&gt;iwctl&lt;/code&gt; utility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iwctl device list
iwctl station [device] get-networks
iwctl station [device] connect [network]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the last command runs, you will be prompted to enter a password if the wifi network is private.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If your network has multiple works, it must be enclosed in quotes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can test your connection by using the &lt;code&gt;ping&lt;/code&gt; command (for example, &lt;code&gt;ping blog.kapic.io&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Advanced: determine the boot mode
&lt;/h4&gt;

&lt;p&gt;Check whether the &lt;code&gt;/sys/firmware/efi/efivars&lt;/code&gt; directory exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls /sys/firmware/efi/efivars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the directory exists, the computer boots with UEFI. If the directory does not exist, it probably boots with BIOS.&lt;/p&gt;

&lt;h4&gt;
  
  
  Optional: update the system clock
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timedatectl set-ntp true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Partition the drive
&lt;/h4&gt;

&lt;p&gt;There are several options for partitioning the drive. You may use whichever you prefer; I suggest &lt;code&gt;cfdisk&lt;/code&gt; because it is a GUI. (&lt;a href="https://youtu.be/HpskN_jKyhc?t=774" rel="noopener noreferrer"&gt;Video reference&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cfdisk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://wiki.archlinux.org/title/Installation_guide" rel="noopener noreferrer"&gt;Arch Linux Installation Guide&lt;/a&gt; suggests using a root and a swap (&amp;gt; 512MiB) partition. If you are booting with UEFI, also create a boot partition.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a filesystem
&lt;/h4&gt;

&lt;p&gt;Filesystem&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkfs.ext4 /dev/[root partition]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkfs.ext4 /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkswap /dev/[swap partition]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkswap /dev/sda2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Mount the partitions
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount /dev/[root partition] /mnt
swapon /dev/[swap partition]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Install essential packages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pacstrap /mnt base linux linux-firmware neovim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: You can choose other text editors in place of neovim. If you do so, use that editor instead of &lt;code&gt;nvim&lt;/code&gt; for the remainder of the tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Fstab
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;genfstab -U /mnt &amp;gt;&amp;gt; /mnt/etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Change root into the new system
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arch-chroot /mnt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Set the timezone
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ln -sf /usr/share/zoneinfo/[continent or country]/[city] /etc/localtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: to see available continents, run &lt;code&gt;ls /usr/share/zoneinfo/&lt;/code&gt;. Likewise, to see available cities, run &lt;code&gt;ls /usr/share/zoneinfo/[continent]/&lt;/code&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hwclock --systohc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Choose locales
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim /etc/locale.gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncomment the locales you wish to use (if you are not sure, just uncomment the line with &lt;code&gt;en_US.UTF-8 UTF-8&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;locale-gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "LANG=[locale]" &amp;gt;&amp;gt; /etc/locale.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "LANG=en_US.UTF-8" &amp;gt;&amp;gt; /etc/locale.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Hostname
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo [hostname] &amp;gt;&amp;gt; /etc/hostname
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/hosts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 [hostname].localdomain [hostname]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Set the root password
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Install useful programs
&lt;/h4&gt;

&lt;p&gt;To be honest, I am not sure what every one of these is for, but I tried installing Arch without them and ran into problems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pacman -S networkmanager network-manager-applet dialog wireless_tools wpa_supplicant os-prober mtools dosfstools base-devel linux-headers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Install a boot loader
&lt;/h4&gt;

&lt;p&gt;Look up which boot loader is correct for the computer. I use &lt;code&gt;grub&lt;/code&gt; for a ThinkPad T530. &lt;strong&gt;This step may be different depending on what hardward is being used. Do your own research to make sure you install a working boot loader.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pacman -S grub
grub-install --target=i386-pc /dev/sda
grub-mkconfig -o /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Exit and reboot
&lt;/h4&gt;



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

umount -a

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

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>My Thoughts on SuperTokens</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Thu, 12 Aug 2021 08:15:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/my-thoughts-on-supertokens-2omm</link>
      <guid>https://dev.to/christopherkapic/my-thoughts-on-supertokens-2omm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BGCvM1w8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/supertokens.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BGCvM1w8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/supertokens.png" alt="SuperTokens Thumbnail" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authentication has been a topic of fascination for me over the last several months. I have been researching all of the available options that I can find. If you follow me on &lt;a href="https://www.youtube.com/channel/UCuXgDzDJhNAwvzvc62GnYwA" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;, then you might have seen my &lt;a href="https://www.youtube.com/watch?v=kPK4Cra_I5I" rel="noopener noreferrer"&gt;video&lt;/a&gt; about authentication methods. Since creating that video, I stumbled upon &lt;a href="https://supertokens.io/" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt;, and I have to say... I am impressed.&lt;/p&gt;

&lt;p&gt;That being said, SuperTokens is for a specific use case—web apps created with &lt;code&gt;create-react-app&lt;/code&gt;. Technically, it works with other libraries as well, but I think other options are better for projects built with Gatsby, other JavaScript frameworks, or native applications—At least for now (I am actually a contributor for SuperTokens, and in the future I plan on working on bringing ease-of-use to Gatsby and maybe Expo for native applications).&lt;/p&gt;

&lt;p&gt;SuperTokens is good for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is simple to set up (with &lt;code&gt;create-react-app&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It is open source.&lt;/li&gt;
&lt;li&gt;You can easily self-host it (I'm working on the CapRover One-Click App).&lt;/li&gt;
&lt;li&gt;It integrates with your Node.js API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the (current) drawbacks of SuperTokens include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are not supported SDKs for other frameworks.&lt;/li&gt;
&lt;li&gt;There are not supported SDKs for other backends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping in mind these limitations, this is the &lt;em&gt;perfect&lt;/em&gt; use case for SuperTokens:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SaaS web application built with CRA on a subdomain&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;You probably do not want to use &lt;code&gt;create-react-app&lt;/code&gt; for a landing page because you want to optimize SEO. So instead, you choose Next.js or Gatsby. But those are annoying to build apps in, so you use &lt;code&gt;create-react-app&lt;/code&gt; for the app itself. Host the app on &lt;code&gt;https://app.yourdomain.com&lt;/code&gt; and the landing page on &lt;code&gt;https://yourdomain.com&lt;/code&gt;. Then &lt;strong&gt;boom&lt;/strong&gt;. Easy authentication with better SEO. And your authentication is not bound by vendor lockin because you can selfhost it.&lt;/p&gt;

&lt;p&gt;This is my current strategy for TickerTab.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Use Gitforks.com to Compare Forks of a Repo</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Tue, 27 Jul 2021 13:47:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/use-gitforks-com-to-compare-forks-of-a-repo-13p3</link>
      <guid>https://dev.to/christopherkapic/use-gitforks-com-to-compare-forks-of-a-repo-13p3</guid>
      <description>&lt;p&gt;One thing that I have wanted to do several times is compare forks of a repository on Github to see who was actually making changes to a repository and who only forked a repository because they thought it would be a fun thing to do. Unfortunately (to the best of my knowledge), Github does not make this easy. In order to see how many commits ahead a repository is, you need to go to the webpage for that repository.&lt;/p&gt;

&lt;p&gt;What I wanted was a quick list, where I could simply scan over it and see which forks had changes. So I built that.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://gitforks.com/" rel="noopener noreferrer"&gt;Gitforks&lt;/a&gt;, a simple website written in React and hosted by Netlify (CDN and one serverless function). The website is &lt;em&gt;really&lt;/em&gt; simple, but it does what I need it to do: display forks and their commits ahead/behind.&lt;/p&gt;

&lt;p&gt;As it stands now, it is not perfect. If I wanted to get fancy, I would add authentication and caching so I do not have to worry about hitting the Github API's rate limit. However, I have better things to do, and it works well enough. If I am honest, my goal is mostly just to have this tool for myself, although I think it would be cool if someone at Github saw this and added it as a feature to the site (please, do this; many of us would find it useful). I also hope that it might be useful to others, so give it a shot and let me know what you think.&lt;/p&gt;

&lt;p&gt;I am also open to having open-source contributions. If you want to make an open source contribution, check out the &lt;a href="https://github.com/christopher-kapic/gitforks" rel="noopener noreferrer"&gt;repo&lt;/a&gt;. You can implement caching (this should not be that difficult) or authentication (I do not know how difficult this is for Github OAuth), or anything else that you think would make for a good addition to the site.&lt;/p&gt;

&lt;p&gt;And for those who just find it a cool idea, I would love if you simply gave the &lt;a href="https://github.com/christopher-kapic/gitforks" rel="noopener noreferrer"&gt;repo&lt;/a&gt; a star. Thank you!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>There is a better alternative for serverless caching</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Wed, 14 Jul 2021 23:21:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/there-is-a-better-alternative-for-serverless-caching-27n0</link>
      <guid>https://dev.to/christopherkapic/there-is-a-better-alternative-for-serverless-caching-27n0</guid>
      <description>&lt;p&gt;The other day, I was really excited because I thought of a great idea for a service: I was going to create a SaaS that handled caching for serverless applications (targeting Netlify specifically, but other platforms could use it as well). In my mind, serverless caching was the one thing that was still lacking from modern serverless stacks, and it was a problem that I needed to solve for &lt;a href="https://tickertab.io/" rel="noopener noreferrer"&gt;TickerTab&lt;/a&gt;. I wrote an &lt;a href="https://blog.kapic.io/grow_netlify_app_with_a_private_api" rel="noopener noreferrer"&gt;article&lt;/a&gt; about how I planned on solving this problem, and funnily enough, I will probably be changing my solution.&lt;/p&gt;

&lt;p&gt;Unfortunately for me (because I wanted to build this business idea), there already is a company providing serverless caching. Enter &lt;a href="https://upstash.com/" rel="noopener noreferrer"&gt;Upstash&lt;/a&gt;, a serverless database for Redis. Upstash has a free tier for up to 10,000 daily commands, which seems amazing, and after that it is only $0.02/100,000 commands. If you want persistance, it is $0.25/GB/month. Not bad.&lt;/p&gt;

&lt;p&gt;Technically speaking, Upstash probably has the insecurity problem I mentioned in my original article about working with a serverless cache, but it still might be worth using for the sake of simplicity. If I choose to migrate TickerTab, my new stack will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Netlify (Hosting, Identity, Functions) - Free until 1,000 MAU, then $100/month&lt;/li&gt;
&lt;li&gt;Stripe (Billing) - 2.9% + $0.30 per successful charge&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://eodhistoricaldata.com/" rel="noopener noreferrer"&gt;EOD Historical Data&lt;/a&gt; - $40/month (for the data necessary for TickerTab)&lt;/li&gt;
&lt;li&gt;Upstash - Free until &amp;gt;10,000 daily commands&lt;/li&gt;
&lt;li&gt;Chat server - Starting at $10/month, hosted on &lt;a href="https://www.vultr.com/?ref=8752906" rel="noopener noreferrer"&gt;Vultr&lt;/a&gt;, and scaling as needed (manually)&lt;/li&gt;
&lt;li&gt;Mailgun - I am not actually sure if I will need this for a pure operational cost, but I anticipate using it to send receipts for Stripe transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In which case I will launch with an operational cost of $50/month until TickerTab grows large enough to have 1,000 MAUs (which will bump me up to $150/month).&lt;/p&gt;

&lt;p&gt;The only other thing I might consider switching is my hosting provider. When I started developing TickerTab, Netlify seemed like the obvious choice, but as I have learned more, it might be the case that &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; is better. If I were to switch to Cloudflare for hosting and functions, I would use &lt;a href="https://supabase.io/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; for authentication. However, I probably will not switch; I mostly just want to be done developing TickerTab since I have worked on it for so long.&lt;/p&gt;

&lt;p&gt;To be honest, I still may try to compete with Upstash because I think there is a lot of opportunity in the market, but I will likely try my other ideas first. I still need to finish working on TickerTab, and I have another idea that could be &lt;em&gt;really good&lt;/em&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to authorize requests for a private API behind a Netlify app</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Mon, 05 Jul 2021 08:17:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/how-to-authorize-requests-for-a-private-api-behind-a-netlify-app-4gpc</link>
      <guid>https://dev.to/christopherkapic/how-to-authorize-requests-for-a-private-api-behind-a-netlify-app-4gpc</guid>
      <description>&lt;p&gt;&lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; is great for developing and hosting static sites. Their authentication service is straightforward relative to other options I have tried (you can get authentication in no time if you are willing to use their identity widget, which is not beautiful, but is good enough for prototyping). They offer cloud functions.&lt;/p&gt;

&lt;p&gt;Long story short, Netlify makes it easy to get a lot of functionality for a website without dealing with many of the hurdles that developers had to deal with in the past.&lt;/p&gt;

&lt;p&gt;However, one thing Netlify does not have an obvious solution for is authorizing requests for private APIs (unless you're on the business plan, in which case, just get your JWT and decode it as middleware on the API). For instance, I wrote an API that requires caching requests. Obviously, with a serverless framework, I cannot count on a Redis instance being available across multiple invocations of a function. Perhaps you saw my &lt;a href="https://blog.kapic.io/how-to-deploy-a-redis-instance" rel="noopener noreferrer"&gt;previous article&lt;/a&gt; in which I propose a way to handle this situation (in not the most secure way—I was desperate). Now, I have four ways to handle this situation. There is also a &lt;a href="https://youtu.be/BxCbAXpgGVk" rel="noopener noreferrer"&gt;video&lt;/a&gt; in which I explain the concepts, if you would prefer that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1 (not that great)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o06ruDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o06ruDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_1.png" title="Serverless function to decode JWT" alt="Option one" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method was my naive first choice when I started development of &lt;a href="https://tickertab.io/" rel="noopener noreferrer"&gt;TickerTab&lt;/a&gt;. It is bad for a couple of reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes it did not work (I do not know if this was my fault or Netlify's)&lt;/li&gt;
&lt;li&gt;If you are going to invoke a serverless function, you might as well do it as outlined in method 3.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 2 (not that great)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--An5Gftcx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--An5Gftcx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_2.png" title="Open cache" alt="Option two" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the method I described in my &lt;a href="https://blog.kapic.io/how-to-deploy-a-redis-instance" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;. It is bad because Redis is super fast, so a brute force attack is possible. Use a long password if you do not want to be hacked, or use a better method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 3 (better—and can be free)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PMqkVyAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PMqkVyAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_3.png" title="Forward requests through serverless function" alt="Option three" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the best solution I have if you want to stay on the free plan. It is essentially the same strategy you would use to authenticate requests to a third-party API, and you can do caching (or any other fancy things that require a true server) on a persistant machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 4 (best, but paid)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XPdYHb8Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XPdYHb8Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/netlify_cache_4.png" title="Get the business plan" alt="Option four" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Netlify Identity uses JWTs, the best solution is to get your secret key and decode the JWTs on your server. This option, however, requires the business plan, so it is fairly expensive. If you are just starting, I would suggest you use method 3, and once you can afford the more expensive plan, switch to method 4. You might have noticed that they are structually quite similar, and switching should require only a minimal amount of refactoring.&lt;/p&gt;

</description>
      <category>netlify</category>
      <category>authentication</category>
      <category>node</category>
      <category>serverless</category>
    </item>
    <item>
      <title>The case for a CapRover Instance</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Sat, 03 Jul 2021 23:33:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/the-case-for-a-caprover-instance-48h1</link>
      <guid>https://dev.to/christopherkapic/the-case-for-a-caprover-instance-48h1</guid>
      <description>&lt;p&gt;If you have not heard of &lt;a href="https://caprover.com/" rel="noopener noreferrer"&gt;CapRover&lt;/a&gt;, it is a self-hosted platform-as-a-service designed to simplify deploying containers; that is well and good, and perhaps you will actually use CapRover at some point to deploy something to production. But the real value is using it as a playground.&lt;/p&gt;

&lt;p&gt;CapRover offers a list of one-click-apps, and unlike choosing from a selection of one-click-apps from a cloud VPS host, you do not need to provision a new machine for each app you deploy. Deployment is super easy, and if there is not a one-click-app available, you can submit a Dockerfile to create your app.&lt;/p&gt;

&lt;p&gt;CapRover also offers &lt;em&gt;ridiculourly easy&lt;/em&gt; SSL deployment, so all of your self-hosted one-click-apps can have valid HTTPS certificates with minimal work on your end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BPcXZ4yX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/one-click-caprover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BPcXZ4yX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.kapic.io/assets/img/one-click-caprover.png" title="One click apps" alt="One click apps" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For context, here are some of my favorite one-click-apps available in the main &lt;a href="https://github.com/caprover/one-click-apps" rel="noopener noreferrer"&gt;CapRover Repository&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ackee.electerious.com/" rel="noopener noreferrer"&gt;Ackee&lt;/a&gt; - a self-hosted, privacy-respecting analytics tool. (An alternative is &lt;a href="https://plausible.io/" rel="noopener noreferrer"&gt;Plausible&lt;/a&gt;, but I have not been able to get the self-hosted version working. If you are willing to pay, their SaaS option is actually pretty good... and I'm not getting paid to say that.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/cdr/code-server" rel="noopener noreferrer"&gt;code-server&lt;/a&gt; - VS Code in your browser. There are a couple quirks, but it is &lt;em&gt;super helpful&lt;/em&gt; to have an accessible (from any computer with a web browser) IDE. Just try not to let yourself get lost in the weeds of maintaining any weird settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.firefly-iii.org/" rel="noopener noreferrer"&gt;Firefly III&lt;/a&gt; - personal finance manager. To be honest, I have yet to spend much time figuring this one out, but it seems promising, and it also seems to be the most popular based on online forums.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.onlyoffice.com/" rel="noopener noreferrer"&gt;OnlyOffice&lt;/a&gt; - OPENOFFICE in the browser. Fairly similar to Microsoft Word in functionality, but in your browser, and open source (not proprietary through Google).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, there are still more one-click-apps for me to explore. There are also a couple that I have had trouble with... If you want Mastodon, do not host it with CapRover (as of July, 2021).&lt;/p&gt;

&lt;p&gt;At the very least, CapRover will offer a little fun. Give it a shot.&lt;/p&gt;

&lt;p&gt;Thanks, and you're welcome.&lt;/p&gt;

</description>
      <category>caprover</category>
    </item>
    <item>
      <title>How to deploy a NodeJS server with NGINX on a VPS</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Thu, 01 Jul 2021 23:22:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/how-to-deploy-a-nodejs-server-with-nginx-on-a-vps-43mb</link>
      <guid>https://dev.to/christopherkapic/how-to-deploy-a-nodejs-server-with-nginx-on-a-vps-43mb</guid>
      <description>&lt;p&gt;This is how I (currently, as of July 2, 2021) deploy NodeJS servers. Eventually I will figure out Docker, but for right now this is good enough. This tutorial also includes information for using &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; on the same VPS for caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prepare a VPS
&lt;/h2&gt;

&lt;p&gt;Create a VPS (I use &lt;a href="https://www.vultr.com/?ref=8752906" rel="noopener noreferrer"&gt;Vultr&lt;/a&gt;–that's my referral link). I use Ubuntu; if you use another distro you will have to use your distro's package manager for installing software.&lt;/p&gt;

&lt;p&gt;You'll probably want a domain name anyways, so point a domain (I will use example.com for the tutorial) at the VPS.&lt;/p&gt;

&lt;p&gt;SSH into the VPS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh root@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Update the machine:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update; sudo apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install programs that will be useful:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install nginx redis-server python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install a text editor (I prefer Neovim):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install neovim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Edit your NGINX files
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim /etc/nginx/sites-available/example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;proxy_pass&lt;/code&gt; value, the port should be whichever port you plan to run your NodeJS server on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. Configure Redis
&lt;/h2&gt;

&lt;p&gt;Edit your &lt;code&gt;redis.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim /etc/redis/redis.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Find the &lt;code&gt;supervised&lt;/code&gt; key; set the value to &lt;code&gt;systemd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Restart Redis:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart redis.service
sudo systemctl restart redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. Install NodeJS (via `nvm)
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Exit your SSH session and start a new one:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh root@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install NodeJS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvm install v15.11.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  5. Clone your repo
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://probablygithub.com/yourusername/yourrepo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. Start your server
&lt;/h2&gt;

&lt;p&gt;Change directory into your server directory:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd yourrepo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install your packages:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Install &lt;code&gt;pm2&lt;/code&gt; globally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install pm2 -g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Start your server (change &lt;code&gt;server.js&lt;/code&gt; to the path of your main file):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pm2 start server.js
pm2 startup
pm2 save
sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  7. Configure SSL
&lt;/h2&gt;

&lt;p&gt;Use LetsEncrypt:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo certbot --nginx -d example.com -d www.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Enter the required information, and soon enough you will have SSL for your server.&lt;/p&gt;

&lt;p&gt;Congrats! You have a deployed NodeJS server with Redis for caching and SSL through LetsEncrypt!&lt;/p&gt;

&lt;h2&gt;
  
  
  Other considerations
&lt;/h2&gt;

&lt;p&gt;You may want to create a different user so you are not running the server as &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may want to use &lt;code&gt;ufw&lt;/code&gt; for added security. I would reference Brad Traversy's &lt;a href="https://gist.github.com/bradtraversy/cd90d1ed3c462fe3bddd11bf8953a896" rel="noopener noreferrer"&gt;deployment strategy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If this tutorial is broken at any point in the process, let me know by leaving a comment below. Thank you!&lt;/p&gt;

</description>
      <category>node</category>
      <category>nginx</category>
      <category>vps</category>
    </item>
    <item>
      <title>How to deploy a Redis instance</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Sat, 19 Jun 2021 08:30:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/how-to-deploy-a-redis-instance-275c</link>
      <guid>https://dev.to/christopherkapic/how-to-deploy-a-redis-instance-275c</guid>
      <description>&lt;p&gt;For part of my development of &lt;a href="https://tickertab.io/" rel="noopener noreferrer"&gt;TickerTab&lt;/a&gt;, I need to create an instance of &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;, an in-memory key:value database for caching. Initially, when I created TickerTab, I had a NodeJS API, but I am refactoring to use Netlify's serverless functions. Since my functions are serverless, I cannot cache on the same server that the functions are run on (like I could with NodeJS), so I need a separate server.&lt;/p&gt;

&lt;p&gt;This is how to set up an independent instance of Redis.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I don't know what I'm doing when it comes to security—if you know something I don't, send me an &lt;a href="//mailto:christopherkapic@gmail.com?subject=REDIS_INSECURITY"&gt;email&lt;/a&gt;. Thanks!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Another note: I now have better ideas about how to handle caching for Netlify. Keep an eye out for another article about a better method.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a VPS
&lt;/h2&gt;

&lt;p&gt;I like to use &lt;a href="https://www.vultr.com/?ref=8752906" rel="noopener noreferrer"&gt;Vultr&lt;/a&gt; (&lt;em&gt;referral link&lt;/em&gt;), and I decided to go with Ubuntu 18.04.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. SSH into the VPS
&lt;/h2&gt;

&lt;p&gt;Then run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt update; apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. Install Redis
&lt;/h2&gt;

&lt;p&gt;Run&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install redis-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  4. Update the configuration
&lt;/h2&gt;

&lt;p&gt;Run&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim /etc/redis/redis.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And change the following:&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;# /etc/redis/redis.conf&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# previously:&lt;/span&gt;
&lt;span class="c"&gt;# supervised no&lt;/span&gt;
&lt;span class="c"&gt;# now:&lt;/span&gt;
supervised systemd

&lt;span class="c"&gt;# previously:&lt;/span&gt;
&lt;span class="nb"&gt;bind &lt;/span&gt;127.0.0.1 ::1
&lt;span class="c"&gt;# now:&lt;/span&gt;
&lt;span class="c"&gt;# bind 127.0.0.1 ::1&lt;/span&gt;
&lt;span class="c"&gt;# NOTE: Generally, this is a big security risk,&lt;/span&gt;
&lt;span class="c"&gt;# but since we are using serverless functions we&lt;/span&gt;
&lt;span class="c"&gt;# don't know which IP address we will receive a&lt;/span&gt;
&lt;span class="c"&gt;# connection from.&lt;/span&gt;

&lt;span class="c"&gt;# previously:&lt;/span&gt;
&lt;span class="c"&gt;# requirepass foobared&lt;/span&gt;
&lt;span class="c"&gt;# now:&lt;/span&gt;
requirepass YOURSUPERSTRONGPASSWORD
&lt;span class="c"&gt;# NOTE: Use a long password. If you don't, Redis&lt;/span&gt;
&lt;span class="c"&gt;# is fast enough that a brute force attack would&lt;/span&gt;
&lt;span class="c"&gt;# not be very difficult.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Run Redis
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl restart redis.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that should do it. I think. If you have problems, leave a comment and I will get back to you!&lt;/p&gt;

</description>
      <category>redis</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Technologies I want to learn this summer</title>
      <dc:creator>CK</dc:creator>
      <pubDate>Wed, 02 Jun 2021 07:51:00 +0000</pubDate>
      <link>https://dev.to/christopherkapic/technologies-i-want-to-learn-this-summer-3n0j</link>
      <guid>https://dev.to/christopherkapic/technologies-i-want-to-learn-this-summer-3n0j</guid>
      <description>&lt;p&gt;Since this is my last summer before I graduate college, I want to take advantage of it to make myself as marketable as possible. It would be useful to have a well-rounded resume, especially as I look for my first post-college job. (By the way, if you are looking for full-stack developers who majored in math at Northwestern, &lt;a href="https://blog.kapic.io/assets/PDF/Christopher_Kapic_Resume_(Lucario).pdf" rel="noopener noreferrer"&gt;here&lt;/a&gt; is my resume).&lt;/p&gt;

&lt;p&gt;Here are the technologies I want to learn and why I want to learn them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I am not artistic, so knowing my way around any CSS framework will fill in that gap well for me. Currently, in spite of all of my efforts, every website I build looks ugly as sin. Hopefully TailwindCSS can change that for me. I know some people have complained about Tailwind making HTML difficult to read, but it seems as though it is pretty universal across different frameworks, which is good.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL
&lt;/h2&gt;

&lt;p&gt;Everyone uses SQL, so I need to learn how to use it too. Based on what I have seen, I do not expect SQL to be difficult. However, it does seem to be the case (from the perspective on one who has not yet used any SQL database) that getting started with a server is difficult, especially if doing so with security in mind. I think I will learn how to use &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; since it seems to be easier to get started with than &lt;a href="https://www.mysql.com/" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt; (which would be my second choice).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It seems to be the case that MongoDB is easier than SQL (again, from the perspective of an outsider), but it also seems to be the case that it is not as ubiquitous as SQL databases. I think Mongo will be fun, and that's the main reason why I plan on learning it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/netlify/gotrue" rel="noopener noreferrer"&gt;GoTrue API&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Authentication is either a pain or requires vendor lock-in. Netlify's GoTrue API might be the easiest solution for a self-hosted, not-too-difficult identity provider. Right now, I use Firebase and Netlify Identity, but GoTrue on a self-hosted machine seems to be ideal for making myself independent on the web. (I will probably still use either Firebase or Netlify Identity for TickerTab.io, just so I do not have to worry about my identity server going down, but I do cringe at the thought of being more dependent on others than necessary for my web presence).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://manjaro.org/" rel="noopener noreferrer"&gt;Manjaro&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I have watched enough Luke Smith. I need to make the jump into Linux. Well, I have already done some work with Linux, but I would like to use it as my daily driver. I have a laptop that runs Archlinux, but I do not care enough about my computer to set up every little detail to be how I want it to be, so using Manjaro seems like a good in-between. I will probably go with the KDE version, and will style it to be as keyboard-centric (eg: avoid using the mouse) as possible, keeping some of the aesthetics of MacOS (which I am using to type this post).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Do I worry about the potential limitations of Svelte? Yes. Do I think it has the potential to be awesome once I learn it? Also yes.&lt;/p&gt;

&lt;p&gt;And I think learning it will be fun. Therefore, I will learn Svelte.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I already have some experience with Stripe, but I would not yet consider myself confident. I do need to implement subscriptions for TickerTab, so there is no choice but to learn Stripe. I think it will be useful for a lot of projects, so I—though daunted—will learn Stripe.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript
&lt;/h2&gt;

&lt;p&gt;It's what the cool kids are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I do not want to need Docker, but something tells me that it will show up, sometime or another. Might as well get ahead of the curve.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>svelte</category>
      <category>stripe</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
