<?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>Forem: Darko Bozhinovski</title>
    <description>The latest articles on Forem by Darko Bozhinovski (@dbozhinovski).</description>
    <link>https://forem.com/dbozhinovski</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%2F996633%2Fa7c3d834-0afb-4647-8354-89f7f8ff4198.png</url>
      <title>Forem: Darko Bozhinovski</title>
      <link>https://forem.com/dbozhinovski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dbozhinovski"/>
    <language>en</language>
    <item>
      <title>The Joy of Astro</title>
      <dc:creator>Darko Bozhinovski</dc:creator>
      <pubDate>Tue, 15 Oct 2024 14:56:34 +0000</pubDate>
      <link>https://forem.com/dbozhinovski/the-joy-of-astro-5501</link>
      <guid>https://forem.com/dbozhinovski/the-joy-of-astro-5501</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Told from the perspective of a person building demo integrations for SuperTokens - and how it's an absolute joy to create with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My love for Astro and the web platform is &lt;a href="https://darko.io/posts/linktree-and-linkinbio-clone-with-astro-and-tinacms" rel="noopener noreferrer"&gt;well&lt;/a&gt; &lt;a href="https://darko.io/posts/wishlist-app-with-astro-and-solid" rel="noopener noreferrer"&gt;documented&lt;/a&gt;. I've contributed to the &lt;a href="https://astro.badg.es/contributor/DBozhinovski/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, I've built some &lt;a href="https://github.com/DBozhinovski/relatinator/tree/master/packages/astro-relatinator" rel="noopener noreferrer"&gt;integrations&lt;/a&gt; and &lt;a href="https://astro.build/themes/details/ltree/" rel="noopener noreferrer"&gt;themes&lt;/a&gt;. I recently rediscovered the joy of building with Astro when I made a demo integration with &lt;a href="https://supertokens.com" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt;. The ultimate goal for it was to become a part of the &lt;code&gt;create-supertokens-app&lt;/code&gt; CLI (published &lt;a href="https://github.com/supertokens/create-supertokens-app" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We'll start with a brief introduction to Astro and the parts relevant to this story and then move on to the integration (including the not-so-nice parts).&lt;/p&gt;

&lt;p&gt;So, let's have a look at what makes Astro an absolute joy to build web stuff with!&lt;/p&gt;

&lt;h2&gt;
  
  
  It's just the web platform - with a nicer API
&lt;/h2&gt;

&lt;p&gt;Astro doesn't reinvent wheels. It gives you a nice and consistent API (or a set of APIs, if you want it pedantic) to work with. You can import anything from plain HTML (via &lt;code&gt;.astro&lt;/code&gt; components) to CSS, components, and scripts... if you can imagine it, Astro can probably import it.&lt;/p&gt;

&lt;p&gt;Everything is static HTML unless you tell it otherwise (more on that later). You get to work with the basic building blocks of the web - HTML, CSS, and JS. But here's the catch - you can make it as complex or as simple as you desire (or your project requires). Want to write TypeScript? It's a CLI command away. Need a modern UI library or framework (don't get me started) for writing components? Take your pick; you can use whatever you like. Yes, Astro supports almost all of them, which is important for integrating SuperTokens.&lt;/p&gt;

&lt;p&gt;Let's have a look at an Astro component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;// This is the frontmatter section where you can define component props and import other components or modules. This is server-side rendered.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt; &lt;span class="na"&gt;tag&lt;/span&gt; &lt;span class="na"&gt;are&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unless&lt;/span&gt; &lt;span class="na"&gt;you&lt;/span&gt; &lt;span class="na"&gt;tell&lt;/span&gt; &lt;span class="na"&gt;them&lt;/span&gt; &lt;span class="na"&gt;otherwise&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  .greeting &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sans&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;f0f0f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"greeting"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to your first Astro component.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  // This is an optional script section where you can add interactivity to your component.
  document.querySelector('.greeting').addEventListener('click', () =&amp;gt; &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You clicked the greeting!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;);
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Remember that static-unless-told-otherwise bit from above? The frontmatter section above illustrates that well - basically, whatever is between those dashes gets executed on the server. You can use the variables defined there inside the HTML. &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags are sort of the exception here - any client-side code there still runs on the client.&lt;/p&gt;

&lt;p&gt;Of course, if you prefer writing CSS in separate files, you can easily use a style tag to import the stylesheet or even use an &lt;code&gt;import&lt;/code&gt; statement in the frontmatter to access those styles inside the component.&lt;/p&gt;

&lt;p&gt;So, in a sense, Astro is just a nicer layer over the standard web platform APIs. But it's also much, much more. If you want it to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components. Yes, all of them.
&lt;/h2&gt;

&lt;p&gt;Whether you're a fan of React, Vue, Svelte, Solid - &lt;a href="https://astro.build/integrations/?search=&amp;amp;categories%5B%5D=frameworks" rel="noopener noreferrer"&gt;Astro's got you&lt;/a&gt;. A quick &lt;code&gt;npx astro add my-framework-of-choice&lt;/code&gt; is all you need. Let's take React as an example.&lt;/p&gt;

&lt;p&gt;First, you need to add it to an existing Astro project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx astro add react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've done this, you can create React components in your Astro project and use them via the &lt;code&gt;import&lt;/code&gt; statement wherever you like (yes, even inside &lt;code&gt;.astro&lt;/code&gt; components).&lt;/p&gt;

&lt;p&gt;To slightly tweak our example above - let's first move the greeting markup to a React component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/components/Greeting.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"greeting"&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You clicked the greeting!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome to your first Astro component using React.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the change above, our &lt;code&gt;.astro&lt;/code&gt; component ends up looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;// Import the React component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/Greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the props for the Astro component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  .greeting &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sans&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;f0f0f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;Use&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;React&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt; &lt;span class="na"&gt;inside&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;Astro&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But here's our first surprise: once we click on the greeting div, we no longer see the alert. This is a feature. As you might remember from above, everything in Astro is static unless we tell it otherwise. That means Astro renders the component on the server and doesn't hydrate any JavaScript along with the component.&lt;/p&gt;

&lt;p&gt;That can be useful, but we still want the alert, right? Astro has a concept called &lt;a href="https://docs.astro.build/en/reference/directives-reference/" rel="noopener noreferrer"&gt;directives&lt;/a&gt; for this and an architectural paradigm called islands (too long to get into here, here's a &lt;a href="https://docs.astro.build/en/concepts/islands/" rel="noopener noreferrer"&gt;source for a deeper dive&lt;/a&gt;). In practice, if we want the component to produce an alert when we click it, we need to add a &lt;code&gt;client:load&lt;/code&gt; (or &lt;a href="https://docs.astro.build/en/reference/directives-reference/#client-directives" rel="noopener noreferrer"&gt;similar&lt;/a&gt; depending on your use case) directive to the React component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;// Import the React component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/Greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the props for the Astro component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  .greeting &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sans&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;f0f0f0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!--&lt;/span&gt; &lt;span class="na"&gt;Use&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;React&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt; &lt;span class="na"&gt;inside&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;Astro&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;interactive&lt;/span&gt; &lt;span class="err"&gt;--&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SSG and SSR
&lt;/h2&gt;

&lt;p&gt;By default, an Astro project compiles to a static site. However, to integrate an auth solution like &lt;a href="https://supertokens.com" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt;, we need a server. Luckily, Astro comes with an &lt;a href="https://docs.astro.build/en/guides/server-side-rendering/" rel="noopener noreferrer"&gt;SSR mode&lt;/a&gt;, which allows us to do server-side processing, too.&lt;/p&gt;

&lt;p&gt;With the SSR mode in place, we have all the Astro-specific pieces we need to integrate SuperTokens with Astro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating SuperTokens' prebuilt UI in Astro
&lt;/h2&gt;

&lt;p&gt;Generally speaking, SuperTokens has two ways of integrating: using the pre-built UI or going the custom route (i.e., using functions and your own UI). The pre-built UI further forks down to two options: the React SDK and the universal pre-built UI. The universal pre-built UI is based on the React one, but it's compiled down to a standalone JS library that can be imported into just about anything.&lt;/p&gt;

&lt;p&gt;Whenever I write an integration for the &lt;code&gt;create-supertokens-app&lt;/code&gt; CLI, I usually pick the universal pre-built UI when working with a non-react frameworks. But remember the bit about Astro working with all components out there? Yep, that means we can use SuperTokens' regular React SDK to make it work with Astro.&lt;/p&gt;

&lt;p&gt;The necessary pieces for a React SDK integration are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A route to render the auth screen/components&lt;/li&gt;
&lt;li&gt;A (server) route to handle the calls to the &lt;a href="https://supertokens.com/docs/thirdpartyemailpassword/introduction#architecture" rel="noopener noreferrer"&gt;SuperTokens auth core&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A home component to show some session info once we're logged in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's have a look at how easy it is to fit that in with Astro.&lt;/p&gt;

&lt;h3&gt;
  
  
  The good
&lt;/h3&gt;

&lt;p&gt;Astro &lt;em&gt;can&lt;/em&gt; be a React framework. And on top of that, Astro can do SSR and API routes without making your head spin. Given the list of requirements above, I just went ahead and copied some files from an existing Remix integration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/supertokens/create-supertokens-app/blob/master/boilerplate/fullstack/astro/src/superTokensHelpers.ts" rel="noopener noreferrer"&gt;superTokensHelpers.ts&lt;/a&gt; - a list of utility functions that help with common tasks. I usually copy and paste this file in most integrations I do, React or otherwise.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/supertokens/create-supertokens-app/blob/master/boilerplate/fullstack/astro/src/components/Root.tsx" rel="noopener noreferrer"&gt;Root.tsx&lt;/a&gt; - a wrapper component, which helps with all things auth on the frontend (specifically, for React components).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/supertokens/create-supertokens-app/blob/master/boilerplate/fullstack/astro/src/components/Home.tsx" rel="noopener noreferrer"&gt;Home.tsx&lt;/a&gt; and &lt;a href="https://github.com/supertokens/create-supertokens-app/blob/master/boilerplate/fullstack/astro/src/components/Auth.tsx" rel="noopener noreferrer"&gt;Auth.tsx&lt;/a&gt; - the formed renders the Home screen (authenticated state) and gives you some session info &lt;em&gt;if&lt;/em&gt; you're authenticated. The latter renders the authentication screen.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/supertokens/create-supertokens-app/blob/master/boilerplate/fullstack/astro/src/components/tryRefreshClientComponent.tsx" rel="noopener noreferrer"&gt;tryRefreshClientComponent.ts&lt;/a&gt;- granted, it's named a bit unconventionally, but it plays a key role in the whole setup - it tries to refresh an existing auth session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these, together with the &lt;a href="https://github.com/supertokens/create-supertokens-app/tree/master/boilerplate/fullstack/astro/src/config" rel="noopener noreferrer"&gt;config&lt;/a&gt; worked quite literally out of the box (and I copy-pasted most of it).&lt;/p&gt;

&lt;p&gt;The server was a bit of a different story, however 😅&lt;/p&gt;

&lt;h3&gt;
  
  
  The not-so-good
&lt;/h3&gt;

&lt;p&gt;Now, it wasn't bad per se. It's just that multi-level catch-all routes don't seem to be a thing in Astro (yet). For example, I couldn't find a way to properly define a &lt;code&gt;*&lt;/code&gt; route in non-file-based routing in Astro.&lt;/p&gt;

&lt;p&gt;Initially, I tried using middleware, but that went nowhere. It led to weird responses, where the middleware caught the route being triggered every time, but that didn't reflect in the response. Getting a valid response and token from the auth core and returning a 404 to the client isn't what I'd call working here.&lt;/p&gt;

&lt;p&gt;Luckily, there is a hacky but easy fix for this - define the multi-level catch-all routes the long way. A picture is a thousand words, so this will certainly explain it better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcneefhymh2c4hcbo85d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcneefhymh2c4hcbo85d.png" alt="Nesting... nesting all the way down." width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Astro, the pages directory is where pages (routes) live. Due to how I had SuperTokens configured, it expected to have full ownerships of the &lt;code&gt;/auth/**&lt;/code&gt; route for frontend and &lt;code&gt;/api/auth/**&lt;/code&gt; for backend calls. However, it also expects to be able to find a route at any of those levels.&lt;/p&gt;

&lt;p&gt;Ultimately, it worked fine, but it took some hacking to make it work. Caveat - I may have misunderstood something, so feel free to &lt;a href="https://x.com/d_bozhinovski" rel="noopener noreferrer"&gt;ping me on Twitter&lt;/a&gt; with what I did wrong. Also, PRs open if that's more your jam 😉&lt;/p&gt;

&lt;h4&gt;
  
  
  A possible alternative
&lt;/h4&gt;

&lt;p&gt;Doing SSR with Astro requires something called an &lt;code&gt;adapter&lt;/code&gt;. The default is node. Now, in theory, you could just add those routes to the node context. Or, as a quick Google search showed me, you can simply change the adapter to &lt;a href="https://github.com/matthewp/astro-fastify" rel="noopener noreferrer"&gt;fastify&lt;/a&gt; to get more routing flexibility. I'll likely try that next!&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Astro can be whatever you want it to be - even a react framework. In a talk, I once called it a meta-metaframework - in a sense, you can build a framework of your own inside Astro. And you can pick products like &lt;a href="https://supertokens.com" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt; off the shelf and integrate them easily.&lt;/p&gt;

&lt;p&gt;So, I'll leave you with this: Use the standards and the platform where they make sense; don't fight them. Webdev can be simpler than we're used to.&lt;/p&gt;

</description>
      <category>astro</category>
      <category>webdev</category>
      <category>react</category>
      <category>supertokens</category>
    </item>
    <item>
      <title>A Better Hammer?</title>
      <dc:creator>Darko Bozhinovski</dc:creator>
      <pubDate>Wed, 02 Oct 2024 12:30:58 +0000</pubDate>
      <link>https://forem.com/dbozhinovski/a-better-hammer-2c26</link>
      <guid>https://forem.com/dbozhinovski/a-better-hammer-2c26</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;It hasn't replaced us. Not yet. But I'm finding ways to make my work easier and faster. Yes, I'm talking about AI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can probably agree that the “untimely death” of the software development profession has, in fact, been grossly exaggerated—no wonder - the hype cycle's gonna hype cycle. I still write software, by every definition of the concept imaginable. I'm using an editor, CLI, and searching through docs. But what changed is that I'm now using another tool to help me out. AI—often a glorified intellisense, sometimes a contextual snippet machine, and infrequently a decent debugger.&lt;/p&gt;

&lt;p&gt;Things have improved since we first considered integrating an LLM into an IDE (or text editor), but, is it good enough to fully replace a developer? Not by a long shot. This approach has many nuances and caveats, so let's dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;...And along came the influencers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of writing on how influencers made it worse, I can leave this pic and call it a day:&lt;/p&gt;

&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%2Ffh72vk9jh5uqjvxcvylj.jpeg" 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%2Ffh72vk9jh5uqjvxcvylj.jpeg" alt="The AI hype replacing the Crypto hype" width="528" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sarcasm aside, if I see one more post on how AI will replace me tomorrow, I swear... Here's what's NOT going to happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You won't just be replaced with AI (no matter what the hottest new AI startup claims)&lt;/li&gt;
&lt;li&gt;AI won't fully write code instead of you (yep, that includes agents. Fight me.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be honest, I looked forward to AI being able to migrate a Next 11 app over to 14 by itself. I'm talking about an app with a couple tens of thousands of lines of (not particularly pretty) code. To date, it hasn't happened, and I don't think it will happen for a while.&lt;/p&gt;

&lt;p&gt;That begs the question, - is it going to? Or better yet, will it replace us as software developers in our lifetimes?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The grossly exaggerated death of the software development profession&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I think statements that include "replacing" developers or "killing the entire profession" are just sensational headlines. I don't think software development, as a craft, trade, or whatever you want to call it, is going anywhere, but it will change.&lt;/p&gt;

&lt;p&gt;Before elaborating further, let me ask you a few questions - has the invention of CAD software replaced engineers and architects? Not really, but it did make them more efficient. Has the invention of tractors replaced farmers? You can tell where I'm going with this. My thesis is - AI will make us more efficient, but much like tractors haven't replaced farmers, AI, &lt;em&gt;as we currently know it&lt;/em&gt; will not replace developers. Barring an AGI breakthrough, I don't think anyone's getting replaced with anything. Quite frankly, LLMs feel like they have peaked (I might have to eat those words, but we'll see), given the decreasing hype in the last few months.&lt;/p&gt;

&lt;p&gt;However, it will change the way we work—in fact, I'd say it already has.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A new way of interacting with code&lt;/strong&gt;
&lt;/h2&gt;

&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%2Fvoyt0y8xfcfyilemjkbl.jpg" 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%2Fvoyt0y8xfcfyilemjkbl.jpg" alt="Crossroads" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Up until a few weeks ago, I was a content user of VSCode. I like that it's FOSS and has a huge ecosystem, and I was okay with paying for Copilot. Additionally, it works on Linux, so I can't complain. That setup was already good—at the very least, it had a better autocomplete than what we previously had. Better autocomplete = more time saved, which in turn means more time available for other pursuits.&lt;/p&gt;

&lt;p&gt;It all changed when I decided to sit in on an engineering weekly we had at &lt;a href="https://supertokens.com/" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt; one day. I'm not a part of the team, but I have to be up to date with what they do, so I listen in on their weeklies when I can. The exciting thing discussed there was &lt;a href="https://cursor.sh/" rel="noopener noreferrer"&gt;https://cursor.sh&lt;/a&gt;. Rishabh, our CTO, was going on about how good it was making things more productive, so I thought, fine, I'll bite. It wasn't the first time I was told Cursor was really good. I have a friend who's been going on about Cursor for months (hi Vlad), and I was kinda skeptical. So, Vlad, you were right. Cursor is… well, pretty impressive.&lt;/p&gt;

&lt;p&gt;From the get-go, it was obvious that it's different from how VSCode works with AI. While both aim to enhance productivity, Cursor takes several steps further in its AI features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Multi-line Auto-completions:&lt;br&gt;
Unlike VSCode's Copilot, which typically offers single-line suggestions, Cursor provides more extensive multi-line auto-completions. This feature can significantly speed up coding by suggesting larger blocks of relevant code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Contextual Awareness:&lt;br&gt;
Cursor's AI &lt;em&gt;seems&lt;/em&gt; to have a deeper understanding of the project context. It considers not just the current file, but also related files and project structure, leading to what feels like more accurate and relevant suggestions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhanced Chat Functionality:&lt;br&gt;
The chat feature in Cursor feels more robust compared to Copilot in VSCode. It can handle more complex queries, provide explanations, and even assist with debugging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Composer Mode:While I haven't had much luck with it, Cursor's Composer mode is a unique feature not present in VSCode. It aims to assist with project-wide tasks such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating entire files or file structures&lt;/li&gt;
&lt;li&gt;Performing large-scale refactoring&lt;/li&gt;
&lt;li&gt;Creating boilerplate code for new projects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What I had luck with, however, was having Cursors generate a front-end app for an invoice generation CLI I had. The CLI itself is a fairly simple idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You feed it JSON.&lt;/li&gt;
&lt;li&gt;You run a script.&lt;/li&gt;
&lt;li&gt;It outputs nicely formatted PDFs to be used as invoices (source &lt;a href="https://github.com/DBozhinovski/invoicer-cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test it's merit, I did two experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Interactive CLI&lt;/strong&gt;—Basically, I asked Cursor to make me an interactive CLI from the really simple one in which I wrote JSON files by hand. It managed to do it pretty smoothly, with a single intervention from my part—styles, which, for some reason, it omitted. Otherwise, my utility suddenly became one that you can run in the CLI, input your data, and get a PDF on the other end. It is certainly an improvement over the initial manual JSON version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web App&lt;/strong&gt; - after that, I asked Cursor to make me a web app based on the CLI. It started off by generating a non-denominational React app, but I had something different in mind - I asked it to make it using Astro and SolidJS instead. While the end result wasn't turnkey, it was really close to being usable. I may have gotten lucky, but I'll repeat the experiment several times to determine whether the experience is consistent. Either way, it was pretty impressive - and stay tuned, that invoicing app is going FOSS very soon 😉&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But Cursor itself aside, these interactions made me realize something else—I'm still creating software. With some more advanced tooling, generated code, and, overall, a better hammer at my disposal—or perhaps a multi-tool of some kind?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Is AI a better hammer? Or a new tool entirely?&lt;/strong&gt;
&lt;/h2&gt;

&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%2F7gx5yvdg3i2hb665g7ag.jpg" 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%2F7gx5yvdg3i2hb665g7ag.jpg" alt="Question mark neon sign" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In its current state? absolutely. The more I think about it, the more I realize that "hammer" might be a misnomer. It's more of a multi-tool, a Swiss army knife of sorts. It helps you with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better auto-completions&lt;/li&gt;
&lt;li&gt;Contextual hints&lt;/li&gt;
&lt;li&gt;Scaffolding things better&lt;/li&gt;
&lt;li&gt;Debugging stuff&lt;/li&gt;
&lt;li&gt;Being a sounding board for your ideas&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, yes, it's a smart tool that helps with multiple modalities of your work. More importantly, it affects how we interact with code. We used to write stuff by hand, now, hitting tab a few times amounts to the same result. It's more efficient, but given the amount of weird or downright wrong suggestions I've had, we're &lt;em&gt;very&lt;/em&gt; far from the tool replacing the operator.&lt;/p&gt;

&lt;p&gt;But here's the kicker - you still have to understand what's going on to make good use of even the smartest tool. LLMs, given they're primarily statistical machines, can make mistakes, and hallucinate... it's still up to the operator to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Can a tool fully replace the operator?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Following the examples throughout history, it certainly hasn't been the rule. Better tools reduce the amount of people needed to perform a task. Farmers, for one, still exist, and we've had huge technological advancements in agriculture since we started growing things from the ground.&lt;/p&gt;

&lt;p&gt;Starting from that point of view, no - AI, as a tool, won't replace programmers. But, given AI's &lt;em&gt;theorized&lt;/em&gt; potential, we can't say for sure. Time will tell 😁&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Instead of a takeaway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As much as AI is a tool, code is, too. As software professionals, our primary function is solving problems via automation. If a better tool came along, it feels fairly irrational to ignore it in the name of preserving the status quo. My point is—learn the new tool and get better at your craft. I, for one, can't wait to NEVER have to write another form validation.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
    <item>
      <title>"BuT, aUtH iS HaRd"</title>
      <dc:creator>Darko Bozhinovski</dc:creator>
      <pubDate>Thu, 08 Aug 2024 14:03:03 +0000</pubDate>
      <link>https://forem.com/dbozhinovski/but-auth-is-hard-1b0m</link>
      <guid>https://forem.com/dbozhinovski/but-auth-is-hard-1b0m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;No, it's not. It's boring, red-tapey, a solved problem... but don't call it hard as a blanket statement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm "I've used MD5 to hash passwords in PHP" years old. Sure, it was a horrible idea, even back in &lt;a href="https://security.stackexchange.com/questions/19906/is-md5-considered-insecure" rel="noopener noreferrer"&gt;2012&lt;/a&gt;. But, back then, I don't remember considering auth "hard." It was a pretty straightforward ordeal by itself - get an email or a username, get a password, hash it (with MD5, as "god intended"), and if you were especially security conscious, &lt;a href="https://en.wikipedia.org/wiki/Salt_(cryptography)" rel="noopener noreferrer"&gt;salt&lt;/a&gt; the password. Store all of that somewhere, usually in a database. Ta-da, signup done.&lt;/p&gt;

&lt;p&gt;Nowadays, the narrative has changed. "Auth is hard" feels like an ever-present narrative that lies a HackerNews or Reddit click away. But is it really? IMO, The truth is, auth isn’t hard to build - anyone can learn it (and everyone in this line of work should learn the basics). The real challenge lies in the extras: MFA, user management, password resets, each of the hundreds of OAuth providers, and account merges from different providers. It’s death by a thousand cuts. Since auth is a solved problem, reinventing the wheel isn’t the best use of your time. But that doesn't mean "auth is hard" as a blanket statement is correct or even close to correct. You should experiment, understand the basics, and build from there. The complexity only grows with the scale (or potential scale) of what you're creating.&lt;/p&gt;

&lt;p&gt;So, how hard can auth really be? Let's dig in.&lt;/p&gt;

&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%2Ft3nvbds0j6ypwh1t4m8h.jpg" 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%2Ft3nvbds0j6ypwh1t4m8h.jpg" alt="Image description" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In the days of yore...
&lt;/h2&gt;

&lt;p&gt;Picking up where I left the story about PHP and md5, building a login functionality followed a similar set of steps; Get an email and a password, check for the existence of the email in your storage, hash the password together with the salt stored for that email, compare the resulting hash with the one stored in the database, and if it all works out fine, set a cookie via &lt;code&gt;setcookie&lt;/code&gt; (we're still in PHP land here - not that the overall logic was too different in other ecosystems).&lt;/p&gt;

&lt;p&gt;Signing out was even simpler - just invalidate the cookie on the server, and you're done. If the server doesn't see a cookie with the next request, you're not logged in. So doing authenticated routes was also a simple ordeal overall. Things could get hairy when it came to permissions, but more often than not, with the apps I had to build, we had just admins and users. Which was something you could simply store together with the user record or in a permissions table if you ever needed to expand the amount of roles you had for your app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Full disclosure—I work for &lt;a href="https://supertokens.com" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt;. This piece, however, is borne out of personal frustration over an omnipresent narrative about how hard auth is as a blanket statement. In other words, I'm not trying to "sell you my thing." Use whatever you like.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Roll your own - a "modern" take
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Email/password and Social auth
&lt;/h3&gt;

&lt;p&gt;To get to where we are today, we'll start at the beginning... Surprising, I know. We can probably agree that these components are enough to make an email/password + social login PoC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A server that handles routes - signup, sign-in, sign-out...&lt;/li&gt;
&lt;li&gt;Some kind of storage to keep the user records (an in-memory array works, too)&lt;/li&gt;
&lt;li&gt;Views - login, signup, and authenticated "dashboard" screens.&lt;/li&gt;
&lt;li&gt;Handlers for social auth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Going with Express and Passport, since we're not going to reinventing wheels, we arrive at exactly 150 lines of very, very dull and repetitive code: &lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs&lt;/a&gt;. The next section will be surface-level explainer of what's going on in the code, so feel free to skip ahead if you're already familiar with the concepts. The express app is a PoC anyway.&lt;/p&gt;

&lt;p&gt;Let's quickly dissect it:&lt;/p&gt;

&lt;h4&gt;
  
  
  Rendering stuff on screen
&lt;/h4&gt;

&lt;p&gt;The way I see it, there are two ways to approach this - start with the rendering and move on to the auth route or the other way around. Mostly by chance, I ended up being an FE-heavy pleb (I can still do SQL, in case you were wondering), so I'll start with the "rendering stuff on-screen" approach.&lt;/p&gt;

&lt;p&gt;Since this is a PoC, we're not gonna go all React-fancy. Plain ol' SSR with &lt;a href="https://ejs.co" rel="noopener noreferrer"&gt;ejs&lt;/a&gt; will do just fine: &lt;a href="https://github.com/supertokens/auth-express/tree/master/views" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/tree/master/views&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding routes
&lt;/h4&gt;

&lt;p&gt;Based on some &lt;a href="https://github.com/passport/todos-express-password/blob/master/package.json" rel="noopener noreferrer"&gt;passport.js&lt;/a&gt; examples, but simplified further, we need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Some deps: &lt;code&gt;npm i passport passport-local express-session&lt;/code&gt;. Let's briefly go over each:

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.passportjs.org/" rel="noopener noreferrer"&gt;Passport.js&lt;/a&gt; - the OG auth middleware for express and node.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.passportjs.org/packages/passport-local/" rel="noopener noreferrer"&gt;passport-local&lt;/a&gt;- an authentication strategy for Passport; An authentication strategy in a module that handles the auth process for a specific auth method - in this case, a local login using a username (email) and password.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/expressjs/session" rel="noopener noreferrer"&gt;express-session&lt;/a&gt; - a middleware that manages session data, allowing you to store and persist user sessions between HTTP requests. It works by assigning a unique session ID to each client, which is stored in a cookie on the client side and used to retrieve session data on the server.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;A place to store our users (the example linked above uses an in-memory array): &lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L13" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L13" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L13&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Configuration for our passport instance and our LocalStrategy instance to handle incoming requests for user lookup: &lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L18" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L18" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L18&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;Initialize passport (&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L60" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L60" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L60&lt;/a&gt;) and express-session (&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L69" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L69" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L69&lt;/a&gt;).&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Verbose, sure. Hard? I don't think so, at least not in the implement-it-as-a-toy sense.&lt;br&gt;
But we moved past using email/password combos a while ago. Let's inspect how hard it is to add a social provider on top of what we have.&lt;/p&gt;

&lt;p&gt;For an example provider here, I decided to go with GitHub for a simple reason—if you decide to fully follow along, it's one of the easiest providers to get started with (looking at you, Google).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you do decide to follow along fully, here's a link describing how to get those GitHub keys: &lt;a href="https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app" rel="noopener noreferrer"&gt;https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app&lt;/a&gt;&lt;br&gt;
Oh, and BTW, the ones in the repo aren't valid, in case you were worried ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Integrating GitHub OAuth2 in our PoC
&lt;/h4&gt;

&lt;p&gt;First off, we need one more dependency, &lt;code&gt;npm i passport-github2&lt;/code&gt;. &lt;a href="https://www.passportjs.org/packages/passport-github2/" rel="noopener noreferrer"&gt;passport-github2&lt;/a&gt; is an auth strategy for Passport, allowing us to integrate with GitHub's OAuth2 API.&lt;/p&gt;

&lt;p&gt;Some handlers (&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L122-L133" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L122-L133&lt;/a&gt;) and configuration (&lt;a href="https://github.com/supertokens/auth-express/blob/master/index.mjs#L29-L45" rel="noopener noreferrer"&gt;https://github.com/supertokens/auth-express/blob/master/index.mjs#L29-L45&lt;/a&gt;) later, well, that's it. Complicated? Probably not. Red-tapey? You bet. Boring? Absolutely. Especially if you get to do it over and over again. It is a solved problem; reinventing wheels is often not the best use of one's time as we've established.&lt;/p&gt;

&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%2Fykp9fwo6aoqivc33nspk.gif" 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%2Fykp9fwo6aoqivc33nspk.gif" alt="Image description" width="498" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The big idea
&lt;/h2&gt;

&lt;p&gt;By now, we can probably agree that Auth isn't hard to &lt;em&gt;build&lt;/em&gt;. Ergo, it's not this magical thing that only white-bearded wizards who speak the mystical language of JWTs can understand and implement.&lt;/p&gt;

&lt;p&gt;No, in fact, I'd argue that as a developer, one should understand the bare basics of how auth works. And I often see a narrative that claims otherwise - something to the tune of "trust me, bro, we can handle that for you". And sure, I agree that for the most part, rolling your own auth is a waste of time. But it's not that hard to build, and it's certainly not a mystical thing. Where it truly becomes hairy is with everything surrounding auth and the user experience.&lt;/p&gt;

&lt;p&gt;Consider this - in the example above, we have a working auth thing. Sort-of. But here's what it can't do (also mentioned in the article opener):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2FA, MFA&lt;/li&gt;
&lt;li&gt;Password resets&lt;/li&gt;
&lt;li&gt;Each and every of the hundreds of OAuth providers with their specificity&lt;/li&gt;
&lt;li&gt;User management&lt;/li&gt;
&lt;li&gt;Account merges from different providers&lt;/li&gt;
&lt;li&gt;Cover all the possible edge cases and potential security holes&lt;/li&gt;
&lt;li&gt;...and I can go on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can probably implement each of these. And on its own, each piece may be considered simple. But it adds up. So, it's not necessarily the implementation—it's maintaining it, being responsible for it, staying up to date with the standards, security breaches, and so on. Plus, a show of hands—how many of you like reading RfCs? I don't imagine I'd see many raised hands if we were on a meetup.&lt;/p&gt;

&lt;p&gt;My point is that auth isn't easy, taken as a whole. Sure, we can easily cobble something together for a PoC, as we did above. But it's not magical, it's not impossible to understand, and please, please don't say that it is. That line of thinking (and marketing), IMO, is damaging to the industry as a whole.&lt;/p&gt;

&lt;p&gt;So, the natural follow-up question would be - when should you roll your own?&lt;/p&gt;

&lt;h3&gt;
  
  
  Toy project, indie, and educational pursuits
&lt;/h3&gt;

&lt;p&gt;...by all means. I'd even encourage it. You learn a lot by doing, so why not? If your indie/toy project or blog grows to have a considerable user base or following, switch it out to a service, a self-hosted solution, or something else. After all, you have a product at that point, and your time will undoubtedly be better spent building that product instead of maintaining auth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Startups
&lt;/h3&gt;

&lt;p&gt;Generally, if you're building products, don't roll your own auth. It's reinventing a very boring and red-tapey wheel. You have plenty of options to choose from. Plus, you are building something, right? Why are we even having this conversation if your product isn't auth?&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaleups and above (however we choose to define them)
&lt;/h3&gt;

&lt;p&gt;Don't. Same reason as startups - but it certainly applies more here.&lt;/p&gt;

&lt;p&gt;You can probably see where I'm going with this. "Auth is hard" is, I'd say, a marketing pitch when used as a blanket statement. You can understand auth, you can build it, but it's boring, it's not fun to maintain, and it's a problem that's solved. Thus, it can be considered a commodity—one that you can pick off the shelf in whichever flavor you choose (some options below).&lt;/p&gt;

&lt;h3&gt;
  
  
  The self-hosted and FOSS landscape
&lt;/h3&gt;

&lt;p&gt;For the ones that are into owning their stack (as yours truly is), you have plenty of options to choose from, too:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Auth libs&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passport.js, covered above in detail&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lucia-auth.com" rel="noopener noreferrer"&gt;Lucia&lt;/a&gt; - A simple and flexible authentication library for modern web applications, focusing on developer experience and ease of use.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://authjs.dev" rel="noopener noreferrer"&gt;Auth.js&lt;/a&gt; - A lightweight and customizable authentication library for Node.js, designed to be easily integrated into various frameworks and applications. Started off as a library for Next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Auth servers&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.keycloak.org" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt; - An open-source identity and access management server offering features like single sign-on (SSO), identity brokering, and user federation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://supertokens.com" rel="noopener noreferrer"&gt;SuperTokens&lt;/a&gt; (see disclaimer above) - An open-source authentication solution providing pre-built features like session management, social login, and email/password authentication with a focus on security and simplicity.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fusionauth.io" rel="noopener noreferrer"&gt;FusionAuth&lt;/a&gt; - A flexible authentication platform catering to developers, offering features like user management, multifactor authentication (MFA), and single sign-on (SSO).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.authelia.com" rel="noopener noreferrer"&gt;Authelia&lt;/a&gt; - An open-source authentication server providing multifactor authentication (MFA) and SSO, designed to secure applications using reverse proxies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Storage + Auth&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://supabase.com" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; - An open-source backend-as-a-service (BaaS) platform providing database, authentication, and real-time capabilities, designed as a Firebase alternative.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pocketbase.io" rel="noopener noreferrer"&gt;Pocketbase&lt;/a&gt; - An open-source backend solution combining database, authentication, and file storage, aimed at simplifying the development of modern web and mobile applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, even if you're not into using third-party software for Auth, you can just pick an open-source one off the shelf, depending on your needs and preferences and roll with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway: auth is the "red tape" of dev
&lt;/h2&gt;

&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%2Fao5nabxake9un1h1i4l6.gif" 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%2Fao5nabxake9un1h1i4l6.gif" alt="Image description" width="480" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My "big" takeaway is to avoid reinventing wheels, especially if it's a solved problem, as auth is. Get educated about said wheels, experiment with them, build a toy wheel, and understand it. But please, please, don't sell it as this impossibly hard thing to understand and build. Educate, don't gatekeep.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>oauth</category>
      <category>node</category>
      <category>express</category>
    </item>
  </channel>
</rss>
